2

I am experiencing an error with AspNetCore.Identity.EntityFrameworkCore 2.1.6 using code-first database migrations. The error is

The entity type 'ModeratedUser' requires a primary key to be defined.

This is confusing me as I have a primary key defined for all entities using Fluent Api. I have done research on this specific issue and all posts I have ran across are referring to different issues (Please note that I have followed the following for defining my many-to-many relationship: https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration)

Here are the entities and the entity type configurations:

public class ModeratedUser { public Guid ModeratedId { get; set; } public virtual List<ModeratorModerated> ModeratorModerated { get; set; } } public class ModeratorUser { public Guid ModeratorId { get; set; } public virtual List<ModeratorModerated> ModeratorModerated { get; set; } } //explicit class to outline many to many between moderated and moderators public class ModeratorModerated { public Guid ModeratorId { get; set; } public Guid ModeratedId { get; set; } public ModeratedUser Moderated { get; set; } public ModeratorUser Moderator { get; set; } } 

Here is the entity type configurations:

public abstract class ModeratedConfiguration : EntityMappingConfiguration<ModeratedUser> { public override void MapToConfig(EntityTypeBuilder<ModeratedUser> builder) { builder.HasKey(x => x.ModeratedId); builder.ToTable("ModeratedUsers", "Mod"); } } public abstract class ModeratorsConfiguration : EntityMappingConfiguration<ModeratorUser> { public override void MapToConfig(EntityTypeBuilder<ModeratorUser> builder) { builder.HasKey(x => x.ModeratorId); builder.ToTable("ModeratorUsers", "Mod"); } } public abstract class ModeratorModeratedConfiguration : EntityMappingConfiguration<ModeratorModerated> { public override void MapToConfig(EntityTypeBuilder<ModeratorModerated> builder) { builder.HasKey(x => new { x.ModeratedId, x.ModeratorId }); builder.HasOne(x => x.Moderated) .WithMany(x => x.ModeratorModerated) .HasForeignKey(x => x.ModeratedId); builder.HasOne(x => x.Moderator) .WithMany(x => x.ModeratorModerated) .HasForeignKey(x => x.ModeratorId); builder.ToTable("ModeratorModerated", "Mod"); } } 

As you can see, I have set the .HasKey for both the ModeratorUser.cs and ModeratedUser.cs and have set the CompositeKey for the ModeratorModerated.cs configuration class.

Here's my Context class:

public class DbContext : IdentityDbContext<ApplicationUser> { public DbContext(DbContextOptions<DbContext> options) : base(options) { } public DbSet<CatalogItem> CatalogItems { get; set; } public DbSet<ModeratedUser> ModeratedUsers { get; set; } public DbSet<ModeratorUser> ModeratorUsers { get; set; } protected override void OnModelCreating(ModelBuilder builder) { builder.HasDefaultSchema("UDC"); builder.AddEntityConfigurationsFromAssembly(GetType().Assembly); base.OnModelCreating(builder); } } 

And here is my ModelBuilderExtensions.cs class that I use to instantiate the EntityMappingConfigurations...

public interface IEntityMappingConfiguration { void MapToConfig(ModelBuilder b); } public interface IEntityMappingConfiguration<T> : IEntityMappingConfiguration where T : class { void MapToConfig(EntityTypeBuilder<T> builder); } public abstract class EntityMappingConfiguration<T> : IEntityMappingConfiguration<T> where T : class { public abstract void MapToConfig(EntityTypeBuilder<T> b); public void MapToConfig(ModelBuilder b) { MapToConfig(b.Entity<T>()); } } public static class ModelBuilderExtenions { private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface) { return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface)); } public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly) { var mappingTypes = assembly.GetMappingTypes(typeof(IEntityMappingConfiguration<>)); foreach (var config in mappingTypes.Select(Activator.CreateInstance).Cast<IEntityMappingConfiguration>()) { config.MapToConfig(modelBuilder); } } } 

The error coming from the PackageManagerConsole window in VS2017 is:

System.InvalidOperationException: The entity type 'ModeratedUser' requires a primary key to be defined.

As you can see, small app but I can't get past my first m-m using EFCore. This wasn't nearly as challenging in EF6.2. What am I missing here?

2
  • 1
    abstract configuration classes - cannot be instantiated, hence the configuration code is not called. Commented Apr 24, 2019 at 2:19
  • Thanks Ivan. I've added my ModelBuilderExtension class which instantiates the abstracts classes. This approach worked fine with other entities that I have removed from the DBContext class for brevity. I am struggling specifically with the entities listed above for some unknown reason. Nice catch. Commented Apr 24, 2019 at 2:42

1 Answer 1

3

Try this changes

public class ModeratedConfiguration : IEntityTypeConfiguration<ModeratedUser> { public void Configure(EntityTypeBuilder<ModeratedUser> builder) { builder.ToTable("ModeratedUsers", "Mod"); builder.HasKey(x => x.ModeratedId); } } public class ModeratorsConfiguration : IEntityTypeConfiguration<ModeratorUser> { public void Configure(EntityTypeBuilder<ModeratorUser> builder) { builder.ToTable("ModeratorUsers", "Mod"); builder.HasKey(x => x.ModeratorId); } } public class ModeratorModeratedConfiguration : IEntityTypeConfiguration<ModeratorModerated> { public void Configure(EntityTypeBuilder<ModeratorModerated> builder) { builder.ToTable("ModeratorModerated", "Mod"); builder.HasKey(x => new { x.ModeratedId, x.ModeratorId }); builder.HasOne(x => x.Moderated) .WithMany(x => x.ModeratorModerated) .HasForeignKey(x => x.ModeratedId); builder.HasOne(x => x.Moderator) .WithMany(x => x.ModeratorModerated) .HasForeignKey(x => x.ModeratorId); } } 

Your DbContext should be

public class DbContext : IdentityDbContext<ApplicationUser> { public DbContext(DbContextOptions<DbContext> options) : base(options) { } public DbSet<CatalogItem> CatalogItems { get; set; } public DbSet<ModeratedUser> ModeratedUsers { get; set; } public DbSet<ModeratorUser> ModeratorUsers { get; set; } protected override void OnModelCreating(ModelBuilder builder) { builder.HasDefaultSchema("UDC"); builder.ApplyConfiguration<ModeratedUser>(new ModeratedUser()); builder.ApplyConfiguration<ModeratedUser>(new ModeratorsConfiguration()); builder.ApplyConfiguration<ModeratorModerated>(new ModeratorModeratedConfiguration()); base.OnModelCreating(builder); } } 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.