Не получается создать кастомную модель БД в IdentityDbContext
Хочу создать свою модель БД для Identity.
Допустим есть Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser, мне хочется унаследовать от него свой тип, где будут содержаться дополнительные поля для моей BL (например, CustomUser).
[Table("user_tbl")]
public class CustomUser : IdentityUser<int>
{
virtual public string CustomProperty { get; set; }
virtual public ICollection<CustomUserRole> UserRoles { get; set; }
virtual public ICollection<CustomUserClaim> UserClaims { get; set; }
virtual public ICollection<CustomUserLogin> UserLogins { get; set; }
virtual public ICollection<CustomUserToken> UserTokens { get; set; }
}
Как вы заметили, тут есть ещё коллекции кастомных ролей, клеймов и т.д. Они так же унаследованы от типов из Microsoft.AspNetCore.Identity.EntityFrameworkCore
[Table("userClaims_tbl")]
public class CustomUserClaim : IdentityUserClaim<int>
{
virtual public CustomUser User { get; set; }
}
[Table("userLogins_tbl")]
public class CustomUserLogin : IdentityUserLogin<int>
{
virtual public CustomUser User { get; set; }
}
[Table("userTokens_tbl")]
public class CustomUserToken : IdentityUserToken<int>
{
virtual public CustomUser User { get; set; }
}
public class CustomRoleClaim : IdentityRoleClaim<int>
{
virtual public CustomRole Role { get; set; }
}
[Table("userRoles_tbl")]
public class CustomUserRole : IdentityUserRole<int>
{
virtual public CustomUser User { get; set; }
virtual public CustomRole Role { get; set; }
}
[Table("roles_tbl")]
public class CustomRole : IdentityRole<int>
{
virtual public string CustomProperty { get; set; }
virtual public ICollection<CustomUserRole> UserRoles { get; set; }
virtual public ICollection<CustomRoleClaim> RoleClaims { get; set; }
}
После этого я создаю контекст базы данных и в методе OnModelCreating() создаю связи между таблицами:
public class CustomDbContext : IdentityDbContext<CustomUser, CustomRole, int,
CustomUserClaim, CustomUserRole, CustomUserLogin, CustomRoleClaim, CustomUserToken>
{
public CustomDbContext() =>
Database.EnsureCreatedAsync();
public CustomDbContext(DbContextOptions options) : base(options) =>
Database.EnsureCreatedAsync();
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<CustomUser>(usr =>
{
usr.HasMany<CustomUserClaim>()
.WithOne(prop => prop.User)
.HasForeignKey("user_id")
.IsRequired();
usr.HasMany<CustomUserLogin>()
.WithOne(prop => prop.User)
.HasForeignKey("user_id")
.IsRequired();
usr.HasMany<CustomUserToken>()
.WithOne(prop => prop.User)
.HasForeignKey("user_id")
.IsRequired();
usr.HasMany<CustomUserRole>()
.WithOne(prop => prop.User)
.HasForeignKey("user_id")
.IsRequired();
});
builder.Entity<CustomRole>(role =>
{
role.HasMany<CustomUserRole>()
.WithOne(prop => prop.Role)
.HasForeignKey("role_id")
.IsRequired();
role.HasMany<CustomRoleClaim>()
.WithOne(prop => prop.Role)
.HasForeignKey("role_id")
.IsRequired();
});
}
}
Далее в классе Startup в методе ConfigureServices добавляю сервисы:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<CustomUser, CustomRole>(options => options.SignIn.RequireConfirmedAccount = true)
.AddDefaultTokenProviders()
.AddDefaultUI()
.AddEntityFrameworkStores<CustomDbContext>();
services.AddRazorPages();
}
После всего мне нужно мигрировать это всё на настоящую БД:
Add-Migration "init"
Update-Database
Самое интересное, когда я создаю БД на дефолтных типах Identity, всё получается без проблем. Что тут не так? Я уже переписываю код как на msdn, но ничего не получается.
Ответы (1 шт):
Вообщем, решение проблемы лежит в переопределении ВСЕЙ модели в методе OnModelCreating()
Если мы наследуем наш контекст данных так:
public class DefaultDbContext : IdentityDbContext<CustomUser, CustomRole, int,
CustomUserClaim, CustomUserRole, CustomUserLogin, CustomRoleClaim, CustomUserToken>
{
//...
}
То и метод OnModelCreating() должен описать ВСЕ типы, с которыми работает контекст:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<CustomUser>(user =>
{
user.ToTable("users_tbl");
user.HasKey(prop => prop.Id);
// if neccessary cusomize CustomProperty
// user.Property(prop => prop.CustomProperty).HasMaxLength...
user.HasMany<CustomUserRole>()
.WithOne(prop => prop.User)
.HasForeignKey(prop => prop.UserId)
.IsRequired();
user.HasMany(prop => prop.UserClaims)
.WithOne(prop => prop.User)
.HasForeignKey(prop => prop.UserId)
.IsRequired();
user.HasMany(prop => prop.UserLogins)
.WithOne(prop => prop.User)
.HasForeignKey(prop => prop.UserId)
.IsRequired();
user.HasMany(prop => prop.UserTokens)
.WithOne(prop => prop.User)
.HasForeignKey(prop => prop.UserId)
.IsRequired();
});
builder.Entity<CustomRole>(role =>
{
role.ToTable("roles_tbl");
// if neccessary cusomize CustomProperty
// user.Property(prop => prop.CustomProperty).HasMaxLength...
role.HasKey(prop => prop.Id);
role.HasMany(prop => prop.UserRoles)
.WithOne(prop => prop.Role)
.HasForeignKey(prop => prop.RoleId)
.IsRequired();
role.HasMany(prop => prop.RoleClaims)
.WithOne(prop => prop.Role)
.HasForeignKey(prop => prop.RoleId)
.IsRequired();
});
builder.Entity<CustomUserRole>(ur =>
{
ur.HasKey(prop => new { prop.RoleId, prop.UserId });
ur.ToTable("userRoles_tbl");
});
builder.Entity<CustomUserClaim>(uc =>
{
uc.HasKey(prop => prop.Id);
uc.ToTable("userClaims_tbl");
});
builder.Entity<CustomUserLogin>(ul =>
{
ul.HasKey(prop => prop.Id);
ul.ToTable("userLogins_tbl");
});
builder.Entity<CustomUserToken>(ut =>
{
ut.HasKey(prop => prop.Id);
ut.ToTable("userTokens_tbl");
});
builder.Entity<CustomRoleClaim>(rc =>
{
rc.HasKey(prop => prop.Id);
rc.ToTable("roleClaims_tbl");
});
}
Так же, вовсе не нужно ставить модификатор virtual для каждого свойства:
[Table("user_tbl")]
public class CustomUser : IdentityUser<int>
{
public string CustomProperty { get; set; }
public ICollection<CustomUserRole> UserRoles { get; set; }
public ICollection<CustomUserClaim> UserClaims { get; set; }
public ICollection<CustomUserLogin> UserLogins { get; set; }
public ICollection<CustomUserToken> UserTokens { get; set; }
}
[Table("userClaims_tbl")]
public class CustomUserClaim : IdentityUserClaim<int>
{
public CustomUser User { get; set; }
}
[Table("userLogins_tbl")]
public class CustomUserLogin : IdentityUserLogin<int>
{
public int Id { get; set; }
public CustomUser User { get; set; }
}
[Table("userTokens_tbl")]
public class CustomUserToken : IdentityUserToken<int>
{
public int Id { get; set; }
public CustomUser User { get; set; }
}
[Table("roleClaims_tbl")]
public class CustomRoleClaim : IdentityRoleClaim<int>
{
public CustomRole Role { get; set; }
}
Атрибуты, как я понял, игнорируются, так что все настройки придется описывать во FluentAPI

