Trí Chồn
Trí Chồn

Reputation: 652

Entity Framework: Cannot be configured on 'xxx' class because it is a derived type

I got this error when try add "AppUserRoles" table by myself to access UserRoles from User, for example:

from u in _userManager.Users.Where(x => x.UserRoles "contain" "some codition here")

And I got this error:

A key cannot be configured on 'AppUserRoles' because it is a derived type. The key must be configured on the root type 'IdentityUserRole'. If you did not intend for 'IdentityUserRole' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.

This is my previous code, It is run ok:

builder.Entity<IdentityUserRole<Guid>>().ToTable("AppUserRoles")
.HasKey(x => new { x.RoleId, x.UserId });

--->And I change to this:

My user class

[Table("AppUsers")]
public class AppUser : IdentityUser<Guid>, IDateTracking, ISwitchable
{
    public virtual ICollection<AppUserRoles> UserRoles { get; set; }
}

My role class

[Table("AppRoles")]
public class AppRole : IdentityRole<Guid>
{
    public virtual ICollection<AppUserRoles> UserRoles { get; set; }
}

My appUserRole class:

[Table("AppUserRoles")]
public class AppUserRoles : IdentityUserRole<Guid>
{

    public virtual AppUser User { get; set; }
    public virtual AppRole Role { get; set; }
}

My DbContextClass

public class AppDbContext : IdentityDbContext<AppUser, AppRole, Guid>
{
    public AppDbContext(DbContextOptions options) : base(options)
    {
    }
    public DbSet<AppUser> AppUsers { get; set; }
    public DbSet<AppRole> AppRoles { get; set; }
    public DbSet<AppUserRoles> AppUserRoles { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        #region Identity Config

        builder.Entity<IdentityUserClaim<Guid>>().ToTable("AppUserClaims").HasKey(x => x.Id);

        builder.Entity<IdentityUserLogin<Guid>>().ToTable("AppUserLogins").HasKey(x => x.UserId);

        builder.Entity<AppUserRoles>(userRole =>
        {
            userRole.HasKey(ur => new { ur.UserId, ur.RoleId });

            userRole.HasOne(ur => ur.Role)
                .WithMany(r => r.UserRoles)
                .HasForeignKey(ur => ur.RoleId)
                .IsRequired();

            userRole.HasOne(ur => ur.User)
                .WithMany(r => r.UserRoles)
                .HasForeignKey(ur => ur.UserId)
                .IsRequired();
        });

        builder.Entity<IdentityUserToken<Guid>>().ToTable("AppUserTokens")
           .HasKey(x => new { x.UserId });

        #endregion Identity Config

    }

    public override int SaveChanges()
    {
        var modified = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Added);

        foreach (EntityEntry item in modified)
        {
            var changedOrAddedItem = item.Entity as IDateTracking;
            if (changedOrAddedItem != null)
            {
                if (item.State == EntityState.Added)
                {
                    changedOrAddedItem.DateCreated = DateTime.Now;
                }
                changedOrAddedItem.DateModified = DateTime.Now;
            }
        }
        return base.SaveChanges();
    }
}

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
    public AppDbContext CreateDbContext(string[] args)
    {
        IConfiguration configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json").Build();
        var builder = new DbContextOptionsBuilder<AppDbContext>();
        var connectionString = configuration.GetConnectionString("DefaultConnection");
        builder.UseSqlServer(connectionString);
        return new AppDbContext(builder.Options);
    }
}

This is my startup file:

services.AddDbContext<AppDbContext>(options =>

options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
            o => o.MigrationsAssembly("YayoiApp.Data.EF")), 
            ServiceLifetime.Scoped);

Please give me some advise, I already have researched to much, but not found a solution. Thanks.

Upvotes: 3

Views: 4295

Answers (1)

Edward
Edward

Reputation: 30016

For custom IdentityUserRole<Guid>, you need to change your DbContext like

public class ApplicationDbContext : IdentityDbContext<AppUser
    , AppRole
    , Guid
    , IdentityUserClaim<Guid>
    , AppUserRoles
    , IdentityUserLogin<Guid>
    , IdentityRoleClaim<Guid>
    , IdentityUserToken<Guid>>
{

Then register it in Startup.cs

services.AddIdentity<AppUser, AppRole>()
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();

UseCase:

public class HomeController : Controller
{
    private readonly UserManager<AppUser> _userManager;
    private readonly RoleManager<AppRole> _roleManager;
    public HomeController(UserManager<AppUser> userManager
        , RoleManager<AppRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }
    public async Task<IActionResult> Index()
    {
        var userName = "Tom";
        var passWord = "1qaz@WSX";
        var appUser = new AppUser
        {
            UserName = userName
        };
        await _userManager.CreateAsync(appUser, passWord);
        var roleName = "Admin";
        await _roleManager.CreateAsync(new AppRole { Name = roleName });
        await _userManager.AddToRoleAsync(appUser, roleName);
        var roles = appUser.UserRoles;
        return View();
    }

Upvotes: 5

Related Questions