فرض کنید قصد داریم متد «SeedDatabaseWithAdminUserAsync» را توسط روش جدید «مقدار دهی اولیهی بانک اطلاعاتی توسط Entity framework Core» بازنویسی کنیم. در ادامه مراحل اینکار را مرور خواهیم کرد.
اضافه کردن نقش پیشفرض Admin
اولین تغییری که در اینجا مورد نیاز است، افزودن نقش پیشفرض Admin است. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی Role برنامه را به نحو زیر انجام میدهیم:
در حین افزودن new Role، دو نکته مدنظر قرار گرفتهاند:
الف) NormalizedName از طریق سرویس ILookupNormalizer در ASP.NET Core Identity تامین میشود.
ب) SiteSettings در اینجا جهت دریافت نام نقش Admin از فایل appsettings.json، تعریف شدهاست (تامین مقدار پیشفرض).
اضافه کردن کاربر پیشفرض Admin
برای اضافه کردن کاربر پیشفرض ادمین، علاوه بر سرویس ILookupNormalizer که کار تامین مقادیر NormalizedEmail و NormalizedUserName را به عهده دارد، نیاز به سرویس IPasswordHasher نیز میباشد. از آن برای تامین مقدار فیلد PasswordHash، بر اساس سرویس هش کردن پسوردهای توکار ASP.NET Core Identity، استفاده میکنیم. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی User برنامه را به نحو زیر انجام میدهیم:
انتساب دادن نقش Admin، به کاربر Admin
تا اینجا نقش ادمین و کاربر ادمین را به صورت مجزا ایجاد کردیم. مرحلهی آخر، انتساب این نقش، به کاربر ادمین است که بر اساس Id این دو صورت میگیرد. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی UserRole برنامه را به نحو زیر انجام میدهیم:
اتصال IEntityTypeConfiguration به DbContext برنامه
IEntityTypeConfigurationهای تهیه شده، دارای سازندههایی هستند که تعدادی سرویس را دریافت میکنند. روش معرفی آنها به این صورت است:
الف) تامین دو سرویس ILookupNormalizer و IPasswordHasher در IDesignTimeDbContextFactory تهیه شده:
در حین انجام عملیات Migration، کار به افزودن و اجرای IEntityTypeConfigurationهای تهیه شده میرسد و این سرویسها قرار است از DbContext تامین شوند؛ به همین جهت نیاز است روش تامین این سرویسها را در همینجا معرفی کنیم.
ب) پس از تامین این سرویسها، روش معرفی آنها به متدهای modelBuilder.ApplyConfiguration در متد OnModelCreating، توسط متد this.GetService به صورت زیر است. متد this.GetService از فضای نام Microsoft.EntityFrameworkCore.Infrastructure تامین میشود و فقط یکبار در زمان Migration از طریق IDesignTimeDbContextFactory تامین خواهد شد:
پس از این تغییرات اگر دستور Migration زیر را صادر کنیم:
مقدار دهی اولیهی نقش و کاربر ادمین و همچنین اتصال این دو را در فایل Migrations\ApplicationDbContextModelSnapshot.cs تولید شده، میتوان مشاهده کرد.
اضافه کردن نقش پیشفرض Admin
اولین تغییری که در اینجا مورد نیاز است، افزودن نقش پیشفرض Admin است. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی Role برنامه را به نحو زیر انجام میدهیم:
namespace ASPNETCoreIdentitySample.DataLayer.Mappings { public class RoleConfiguration : IEntityTypeConfiguration<Role> { private readonly SiteSettings _siteSettings; private readonly ILookupNormalizer _keyNormalizer; public RoleConfiguration(SiteSettings siteSettings, ILookupNormalizer keyNormalizer) { _siteSettings = siteSettings ?? throw new ArgumentNullException(nameof(siteSettings)); _keyNormalizer = keyNormalizer ?? throw new ArgumentNullException(nameof(keyNormalizer)); } public void Configure(EntityTypeBuilder<Role> builder) { builder.ToTable("AppRoles"); var adminUserSeed = _siteSettings.AdminUserSeed; builder.HasData( new Role { Id = 1, Name = adminUserSeed.RoleName, NormalizedName = _keyNormalizer.NormalizeName(adminUserSeed.RoleName), ConcurrencyStamp = Guid.NewGuid().ToString() }); } } }
الف) NormalizedName از طریق سرویس ILookupNormalizer در ASP.NET Core Identity تامین میشود.
ب) SiteSettings در اینجا جهت دریافت نام نقش Admin از فایل appsettings.json، تعریف شدهاست (تامین مقدار پیشفرض).
اضافه کردن کاربر پیشفرض Admin
برای اضافه کردن کاربر پیشفرض ادمین، علاوه بر سرویس ILookupNormalizer که کار تامین مقادیر NormalizedEmail و NormalizedUserName را به عهده دارد، نیاز به سرویس IPasswordHasher نیز میباشد. از آن برای تامین مقدار فیلد PasswordHash، بر اساس سرویس هش کردن پسوردهای توکار ASP.NET Core Identity، استفاده میکنیم. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی User برنامه را به نحو زیر انجام میدهیم:
public class UserConfiguration : IEntityTypeConfiguration<User> { private readonly SiteSettings _siteSettings; private readonly ILookupNormalizer _keyNormalizer; private readonly IPasswordHasher<User> _passwordHasher; public UserConfiguration( SiteSettings siteSettings, ILookupNormalizer keyNormalizer, IPasswordHasher<User> passwordHasher) { _siteSettings = siteSettings ?? throw new ArgumentNullException(nameof(siteSettings)); _keyNormalizer = keyNormalizer ?? throw new ArgumentNullException(nameof(keyNormalizer)); _passwordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher)); } public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("AppUsers"); var adminUserSeed = _siteSettings.AdminUserSeed; builder.HasData( new User { Id = 1, UserName = adminUserSeed.Username, NormalizedUserName = _keyNormalizer.NormalizeName(adminUserSeed.Username), Email = adminUserSeed.Email, NormalizedEmail = _keyNormalizer.NormalizeEmail(adminUserSeed.Email), EmailConfirmed = true, IsEmailPublic = true, LockoutEnabled = true, TwoFactorEnabled = false, PasswordHash = _passwordHasher.HashPassword(null, adminUserSeed.Password), ConcurrencyStamp = Guid.NewGuid().ToString(), SecurityStamp = string.Empty, IsActive = true }); } }
انتساب دادن نقش Admin، به کاربر Admin
تا اینجا نقش ادمین و کاربر ادمین را به صورت مجزا ایجاد کردیم. مرحلهی آخر، انتساب این نقش، به کاربر ادمین است که بر اساس Id این دو صورت میگیرد. برای این منظور توسط یک IEntityTypeConfiguration جدید، تنظیمات موجودیت سفارشی UserRole برنامه را به نحو زیر انجام میدهیم:
public class UserRoleConfiguration : IEntityTypeConfiguration<UserRole> { public void Configure(EntityTypeBuilder<UserRole> builder) { builder.HasOne(userRole => userRole.Role) .WithMany(role => role.Users) .HasForeignKey(userRole => userRole.RoleId); builder.HasOne(userRole => userRole.User) .WithMany(user => user.Roles) .HasForeignKey(userRole => userRole.UserId); builder.ToTable("AppUserRoles"); builder.HasData( new UserRole { UserId = 1, RoleId = 1 }); } }
اتصال IEntityTypeConfiguration به DbContext برنامه
IEntityTypeConfigurationهای تهیه شده، دارای سازندههایی هستند که تعدادی سرویس را دریافت میکنند. روش معرفی آنها به این صورت است:
الف) تامین دو سرویس ILookupNormalizer و IPasswordHasher در IDesignTimeDbContextFactory تهیه شده:
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext> { public ApplicationDbContext CreateDbContext(string[] args) { // .... services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>(); services.TryAddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
ب) پس از تامین این سرویسها، روش معرفی آنها به متدهای modelBuilder.ApplyConfiguration در متد OnModelCreating، توسط متد this.GetService به صورت زیر است. متد this.GetService از فضای نام Microsoft.EntityFrameworkCore.Infrastructure تامین میشود و فقط یکبار در زمان Migration از طریق IDesignTimeDbContextFactory تامین خواهد شد:
using Microsoft.EntityFrameworkCore.Infrastructure; namespace ASPNETCoreIdentitySample.DataLayer.Context { public class ApplicationDbContext : IdentityDbContext<User, Role, int, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>, IUnitOfWork { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); var siteSettings = this.GetService<IOptionsSnapshot<SiteSettings>>()?.Value; var lookupNormalizer = this.GetService<ILookupNormalizer>(); var passwordHasher = this.GetService<IPasswordHasher<User>>() modelBuilder.ApplyConfiguration(new RoleConfiguration(siteSettings, lookupNormalizer)); modelBuilder.ApplyConfiguration(new UserConfiguration(siteSettings, lookupNormalizer, passwordHasher)); modelBuilder.ApplyConfiguration(new UserUsedPasswordConfiguration(siteSettings, passwordHasher)); // ...
پس از این تغییرات اگر دستور Migration زیر را صادر کنیم:
dotnet ef migrations --startup-project ../ASPNETCoreIdentitySample/ add V1