PDA

View Full Version : ایجاد رابطه کلاس های خودمان با کلاس های Identity چگونه است



sayanpro
یک شنبه 19 مرداد 1399, 08:48 صبح
سلام دوستان وقت بخیر.
فرض کنید کلاسی به نام Customer داریم، Identity هم در پروژه فعال کردیم. AspNetUsers هم که اطلاعات کاربران سایت در خودش نگه داری میکند.

آیا برای ایجاد رابطه بین کلاس Customer, AspNetUsers باید در Migration رابطه یک به یک آنها را تعریف کنیم؟ اصولا این کار منطقی است؟ چه زمان هایی باید رابطه ها در Migration تعریف کنیم؟ چرا مثلا در FluentApi این کار نمیکنیم؟

ممنون میشم راهنمایی بفرمائید.

مهدی کرامتی
دوشنبه 20 مرداد 1399, 18:54 عصر
تو این جور سناریو ها من معمولا یک کلاس 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<ApplicationDbContext> 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("NormalizedUsername");
});

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("RoleNormalizedName");
});

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 گرفتم. همچنین، اسم جدول های فوق، و اسم ستون های اصلی کلید رو هم عوض کردم.

sayanpro
سه شنبه 21 مرداد 1399, 11:36 صبح
تو این جور سناریو ها من معمولا یک کلاس User از IdentityUser ارث بری می کنم (مثلا اسمش رو میگذارم ApplicationUser) و موقع تعریف وفعال کردن Identity،

با عرض سلام، آقای کرامتی عزیز، از راهنمایی دقیق شما خیلی ممنون هستم. من توی پروژه ای دیدم که مثلا برقراری رابطه applicationUser با Order در migration تعریف کرده بود! و از fluent استفاده نکرده بود. به نظرم استفاده از migration برای ایجاد رابطه بین کلاس ها، منطق کار و نگهداری کد خیلی سخت میکنه.

مهدی کرامتی
سه شنبه 21 مرداد 1399, 12:20 عصر
نه تنها کار سختی است، بلکه روش اصولی ای نیست.