Search code examples
c#entity-framework-coreef-code-first

The entity type 'List<Workout>' requires a primary key to be defined


My problem is I get this error

The entity type 'List' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types, see https://go.microsoft.com/fwlink/?linkid=2141943."

I searched about it and did everything.

id is public, and the data annotation [Key] is there. I don't know what else to do.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Business_Layer
{
    public class ExerciseType
    {
        [Key]
        public int Id { get; set; }
        [Required]
        public string Name { get; set; }

        [Required]
        public string Description { get; set; }

        [Required]
        public BodyParts TargetetMuscle { get; set; }

        public Equipment Equipment { get; set; }

        public List<Exercise> Exercises { get; set; }

        public ExerciseType()
        {
             Exercises = new List<Exercise>();
        }

        public ExerciseType(string name, string description, BodyParts targetedmuscle, Equipment equipment)
        {
            Exercises = new List<Exercise>();
            Name = name;
            Description = description;
            TargetetMuscle = targetedmuscle;
            Equipment = equipment;
        }
    }
}
namespace Business_Layer
{
    public class Exercise
    {
        [Key]
        public int Id { get; set; }
        [Required]
        public int Weight { get; set; }
        [Required]
        public int Reps { get; set; }
        [Required]
        public Status status { get; set; }
        public ExerciseType exerciseType { get; set; }
        public List<Workout> workouts { get; set; }

        public Exercise()
        {
           workouts = new List<Workout>();
        }

        public Exercise(string name, string description,int weight, int reps)
        {
            status = Status.Waiting;
            Weight = weight;
            Reps = reps;
            workouts = new List<Workout>();
        }
    }
}
namespace Business_Layer
{
    public class User
    {
        [Key]
        public string Id { get; set; }

        [Required]
        [MaxLength(50, ErrorMessage = "Name cannot be more than 50 symbols!")]
        public string Name { get; set; }

        public List<Workout> Workouts { get; set; }
        public List<Workout> History { get; set; }
      
        public User()
        {
             History = new List<Workout>();
             Workouts = new List<Workout>();
        }

        public User(string name)
        {   
            Id = Guid.NewGuid().ToString();
            Name = name;

           History = new List<Workout>();
           Workouts = new List<Workout>();
        }
    }
}
namespace Business_Layer
{
    public class Workout
    {
        [Key]
        public int WorkoutId { get; set; }

        public List<Exercise> Exercises { get; set; }

        public User Creator { get; set; }
        public List<User> Users { get; set; }
        public Status Status { get; set; }

        public Workout()
        {
           Exercises = new List<Exercise>();
        }

        public Workout(User user)
        {
            Status = Status.Waiting;
            Users = new List<User>();
            Exercises = new List<Exercise>();
        }
    }
}
public class TrackerDBContext:DbContext
{
    public TrackerDBContext()
    {
    }

    public TrackerDBContext(DbContextOptions contextOptions) : base(contextOptions)
    {
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=DESKTOP-PTKBD3O\\SQLEXPRESS01;Database=FitnessTrackerDB;Trusted_Connection=True;");
        base.OnConfiguring(optionsBuilder);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ExerciseType>()
                    .Property(a => a.TargetetMuscle)
                    .HasConversion<string>();

        modelBuilder.Entity<ExerciseType>()
                    .Property(o => o.Equipment)
                    .HasConversion<string>();

        modelBuilder.Entity<User>()
                    .HasMany(e => e.Workouts)
                    .WithMany(e=>e.Users);//napravi vruzka mejdu history i workouts

        modelBuilder.Entity<User>()
                    .HasOne(e => e.History);

        modelBuilder.Entity<Workout>()
                    .HasOne(e => e.Creator);

        base.OnModelCreating(modelBuilder);
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Workout> Workouts { get; set; }
    public DbSet<Exercise> Exercises { get; set; }
    public DbSet<ExerciseType> ExerciseTypes { get; set; }
}

Solution

  • This line is the culprit:

    modelBuilder.Entity<User>()
                .HasOne(e => e.History);
    

    Since e.History is a List<T>, but mapped as a reference (HasOne), EF interprets the type as a mapped type. Then it looks for a property that qualifies as a primary key according to EF's name conventions, and finds none.

    Obviously, the mapping should be:

    modelBuilder.Entity<User>()
                .HasMany(e => e.History);
    

    Adhering to common naming conventions (use plural names for collection properties) might have helped prevent this error.