تو این جور سناریو ها من معمولا یک کلاس User از IdentityUser ارث بری می کنم (مثلا اسمش رو میگذارم ApplicationUser) و موقع تعریف وفعال کردن Identity، این کلاس رو بعنوان نماینده جدول و موجودیت Users معرفی می کنم. در این حالت، دستت باز خواهد بود که در کلاس های دیگه از طریق تعریف پراپرتی ای از جنس ApplicationUser و البته، تعبیه کردن پراپرتی UserID، بصورتی که جنس اون پراپرتی با جنس کلید ApplicationUser یکسان باشه، مثلا اگر پراپرتی کلید در ApplicationUser از جنس Guid، یا String، یا int است، جنس این پراپرتی هم همون باشه، بین موجودیت User و کلاس/موجودیت مورد نظر ارتباط برقرار می کنم.
برای انجام کاری که گفتم، در کلاس ApplcationDbContext، در رویداد override شده OnModelCreating این تغییرات رو دادم:
public class ApplicationDbContext : IdentityDbContext<
ApplicationUser,
ApplicationRole,
int,
ApplicationUserClaim,
ApplicationUserRole,
ApplicationUserLogin,
ApplicationRoleClaim,
ApplicationUserToken> // Mehdi: Class declaration modified
{
public ApplicationDbContext(DbContextOptions<ApplicationD bContext> options)
: base(options)
{
}
public DbSet<Product> Product { get; set; }
public DbSet<ProductCategory> ProductCategory { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne(e => e.User)
.HasForeignKey(uc => uc.UserId)
.IsRequired();
// Each User can have many UserLogins
b.HasMany(e => e.Logins)
.WithOne(e => e.User)
.HasForeignKey(ul => ul.UserId)
.IsRequired();
// Each User can have many UserTokens
b.HasMany(e => e.Tokens)
.WithOne(e => e.User)
.HasForeignKey(ut => ut.UserId)
.IsRequired();
// Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
modelBuilder.Entity<ApplicationRole>(b =>
{
// Each Role can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
// Each Role can have many associated RoleClaims
b.HasMany(e => e.RoleClaims)
.WithOne(e => e.Role)
.HasForeignKey(rc => rc.RoleId)
.IsRequired();
});
modelBuilder.Entity<ApplicationUser>(b =>
{
b.ToTable("Users");
b.Property(e => e.Id).HasColumnName("UserID");
b.Property(e => e.UserName).HasColumnName("Username");
b.Property(e => e.NormalizedUserName).HasColumnName("NormalizedUse rname");
});
modelBuilder.Entity<ApplicationUserClaim>(b =>
{
b.ToTable("UserClaims");
b.Property(e => e.Id).HasColumnName("UserClaimID");
b.Property(e => e.UserId).HasColumnName("UserID");
});
modelBuilder.Entity<ApplicationUserLogin>(b =>
{
b.ToTable("UserLogins");
b.Property(e => e.UserId).HasColumnName("UserID");
});
modelBuilder.Entity<ApplicationUserToken>(b =>
{
b.ToTable("UserTokens");
b.Property(e => e.UserId).HasColumnName("UserID");
});
modelBuilder.Entity<ApplicationRole>(b =>
{
b.ToTable("Roles");
b.Property(e => e.Id).HasColumnName("RoleID");
b.Property(e => e.Name).HasColumnName("RoleName");
b.Property(e => e.NormalizedName).HasColumnName("RoleNormalizedNam e");
});
modelBuilder.Entity<ApplicationRoleClaim>(b =>
{
b.ToTable("RoleClaims");
b.Property(e => e.Id).HasColumnName("RoleClaimID");
b.Property(e => e.RoleId).HasColumnName("RoleID");
});
modelBuilder.Entity<ApplicationUserRole>(b =>
{
b.ToTable("UserRoles");
});
}
}
کلاس های اصلاح شده مدل های مرتبط با Identity ام هم، اینهاست:
public class ApplicationUser : IdentityUser<int>
{
// Additional Properties
public virtual ICollection<ApplicationUserClaim> Claims { get; set; }
public virtual ICollection<ApplicationUserLogin> Logins { get; set; }
public virtual ICollection<ApplicationUserToken> Tokens { get; set; }
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class ApplicationRole : IdentityRole<int>
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
public virtual ICollection<ApplicationRoleClaim> RoleClaims { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<int>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationUserClaim : IdentityUserClaim<int>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationUserLogin : IdentityUserLogin<int>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationRoleClaim : IdentityRoleClaim<int>
{
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationUserToken : IdentityUserToken<int>
{
public virtual ApplicationUser User { get; set; }
}
همانطور که در تعریف کلاس ApplicationUser مشخص است، در اون کلاس یک رابطه به کلاس Orders تعریف کردم، بالطبع در کلاس Orders هم ستون های مربوطه به این کلاس رو اضافه کردم، به این شکل:
public class Order
{
[Key]
public int OrderID { get; set; }
[Display(Name = "کاربر")]
public int UserID { get; set; }
[Display(Name = "تاریخ سفارش")]
[Required(ErrorMessage = "لطفا {0} را وارد کنید")]
public DateTime OrderDate { get; set; }
[Display(Name = "نهایی شده")]
public bool OrderIsFinalized { get; set; }
// Navigation Properties
[ForeignKey("UserID")]
public virtual ApplicationUser User { get; set; }
}
در کد فوق، هم پراپرتی UserID از جنس int تعریف شده، هم پراپرتی User، از جنس ApplicationUser، بالایش هم با افزودن صفت ForeignKey مشخص کردم که رابطه با کلاس User از طریق کدوم پراپرتی برقرار میشه. این ترکیب از ساخته شدن ستون ناخواسته اضافی در جدولهای مربوطه، جهت حفظ ارتباط جلوگیری می کنه و میگه که مقدار رکورد Parent در جدول Users در کدام ستون در جدول فرزند (Orders) نگهداری شود.
و در نهایت، در فایل Startup، به این صورت در متد ConfigureServices این طوری اعلام کردم که کلاس User و Role ام چی هاست:
services.AddIdentity<ApplicationUser, ApplicationRole>()
تغییر عمده ای که در ترکیبات فوق دیده میشه اینه که در همه جداول مربوط به Identity، جنس ستون کلید رو int گرفتم. همچنین، اسم جدول های فوق، و اسم ستون های اصلی کلید رو هم عوض کردم.