سفارشی سازی ASP.NET Core Identity - قسمت دوم - سرویس‌های پایه
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سیزده دقیقه

در قسمت قبل کلاس‌های متناظر با جداول پایه‌ی ASP.NET Core Identity را تغییر دادیم. اما هنوز سرویس‌های پایه‌ی این فریم ورک مانند مدیریت و ذخیره‌ی کاربران و مدیریت و ذخیره‌ی نقش‌ها، اطلاعی از وجود آن‌ها ندارند. در ادامه این سرویس‌ها را نیز سفارشی سازی کرده و سپس به ASP.NET Core Identity معرفی می‌کنیم.

سفارشی سازی RoleStore

ASP.NET Core Identity دو سرویس را جهت کار با نقش‌های کاربران پیاده سازی کرده‌است:
- سرویس RoleStore: کار آن دسترسی به ApplicationDbContext ایی است که در قسمت قبل سفارشی سازی کردیم و سپس ارائه‌ی زیر ساختی به سرویس RoleManager جهت استفاده‌ی از آن برای ذخیره سازی و یا تغییر و حذف نقش‌های سیستم.
وجود Storeها از این جهت است که اگر علاقمند بودید، بتوانید از سایر ORMها و یا زیرساخت‌های ذخیره سازی اطلاعات برای کار با بانک‌های اطلاعاتی استفاده کنید. در اینجا از همان پیاده سازی پیش‌فرض آن که مبتنی بر EF Core است استفاده می‌کنیم. به همین جهت است که وابستگی ذیل را در فایل project.json مشاهده می‌کنید:
   "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.1.0",
- سرویس RoleManager: این سرویس از سرویس RoleStore و تعدادی سرویس دیگر مانند اعتبارسنج نام نقش‌ها و نرمال ساز نام نقش‌ها، جهت ارائه‌ی متدهایی برای یافتن، افزودن و هر نوع عملیاتی بر روی نقش‌ها استفاده می‌کند.
برای سفارشی سازی سرویس‌های پایه‌ی ASP.NET Core Identity‌، ابتدا باید سورس این مجموعه را جهت یافتن نگارشی از سرویس مدنظر که کلاس‌های موجودیت را به صورت آرگومان‌های جنریک دریافت می‌کند، پیدا کرده و سپس از آن ارث بری کنیم:
public class ApplicationRoleStore :
     RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>,
     IApplicationRoleStore
تا اینجا مرحله‌ی اول تشکیل کلاس ApplicationRoleStore سفارشی به پایان می‌رسد. نگارش جنریک RoleStore پایه را یافته و سپس موجودیت‌های سفارشی سازی شده‌ی خود را به آن معرفی می‌کنیم.
این ارث بری جهت تکمیل، نیاز به بازنویسی سازنده‌ی RoleStore پایه را نیز دارد:
public ApplicationRoleStore(
   IUnitOfWork uow,
   IdentityErrorDescriber describer = null)
   : base((ApplicationDbContext)uow, describer)
در اینجا پارامتر اول آن‌را به IUnitOfWork بجای DbContext متداول تغییر داده‌ایم؛ چون IUnitOfWork دقیقا از همین نوع است و همچنین امکان دسترسی به متدهای ویژه‌ی آن‌را نیز فراهم می‌کند.
در نگارش 1.1 این کتابخانه، بازنویسی متد CreateRoleClaim نیز اجباری است تا در آن مشخص کنیم، نحوه‌ی تشکیل کلید خارجی و اجزای یک RoleClaim به چه نحوی است:
        protected override RoleClaim CreateRoleClaim(Role role, Claim claim)
        {
            return new RoleClaim
            {
                RoleId = role.Id,
                ClaimType = claim.Type,
                ClaimValue = claim.Value
            };
        }
در نگارش 2.0 آن، این new RoleClaim به صورت خودکار توسط کتابخانه صورت خواهد گرفت و سفارشی کردن آن ساده‌تر می‌شود.

در ادامه اگر به انتهای تعریف امضای کلاس دقت کنید، یک اینترفیس IApplicationRoleStore را نیز مشاهده می‌کنید. برای تشکیل آن بر روی نام کلاس سفارشی خود کلیک راست کرده و با استفاده از ابزارهای Refactoring کار Extract interface را انجام می‌دهیم؛ از این جهت که در سایر لایه‌های برنامه نمی‌خواهیم از تزریق مستقیم کلاس ApplicationRoleStore استفاده کنیم. بلکه می‌خواهیم اینترفیس IApplicationRoleStore را در موارد ضروری، به سازنده‌های کلاس‌های تزریق نمائیم.
پس از تشکیل این اینترفیس، مرحله‌ی بعد، معرفی آن‌ها به سیستم تزریق وابستگی‌های ASP.NET Core است. چون تعداد تنظیمات مورد نیاز ما زیاد هستند، یک کلاس ویژه به نام IdentityServicesRegistry تشکیل شده‌است تا به عنوان رجیستری تمام سرویس‌های سفارشی سازی شده‌ی ما عمل کند و تنها با فراخوانی متد AddCustomIdentityServices آن در کلاس آغازین برنامه، کار ثبت یکجای تمام سرویس‌های ASP.NET Core Identity انجام شود و بی‌جهت کلاس آغازین برنامه شلوغ نگردد.
ثبت ApplicationDbContext طبق روش متداول آن در کلاس آغازین برنامه انجام شده‌است. سپس معرفی سرویس IUnitOfWork را که از ApplicationDbContext تامین می‌شود، در کلاس IdentityServicesRegistry مشاهده می‌کنید.
 services.AddScoped<IUnitOfWork, ApplicationDbContext>();
در ادامه RoleStore سفارشی ما نیاز به دو تنظیم جدید را خواهد داشت:
services.AddScoped<IApplicationRoleStore, ApplicationRoleStore>();
services.AddScoped<RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>, ApplicationRoleStore>();
ابتدا مشخص کرده‌ایم که اینترفیس IApplicationRoleStore، از طریق کلاس سفارشی ApplicationRoleStore تامین می‌شود.
سپس RoleStore توکار ASP.NET Core Identity را نیز به ApplicationRoleStore خود هدایت کرده‌ایم. به این ترتیب هر زمانیکه در کدهای داخلی این فریم ورک وهله‌ای از RoleStore جنریک آن درخواست می‌شود، دیگر از همان پیاده سازی پیش‌فرض خود استفاده نخواهد کرد و به پیاده سازی ما هدایت می‌شود.

این روشی است که جهت سفارشی سازی تمام سرویس‌های پایه‌ی ASP.NET Core Identity بکار می‌گیریم:
1) ارث بری از نگارش جنریک سرویس پایه‌ی موجود و معرفی موجودیت‌های سفارشی سازی شده‌ی خود به آن
2) سفارشی سازی سازنده‌ی این کلاس‌ها با سرویس‌هایی که تهیه کرده‌ایم (بجای سرویس‌های پیش فرض).
3) تکمیل متدهایی که باید به اجبار پس از این ارث بری پیاده سازی شوند.
4) استخراج یک اینترفیس از کلاس نهایی تشکیل شده (توسط ابزارهای Refactoring).
5) معرفی اینترفیس و همچنین نمونه‌ی توکار این سرویس به سیستم تزریق وابستگی‌های ASP.NET Core جهت استفاده‌ی از این سرویس جدید سفارشی سازی شده.


سفارشی سازی RoleManager

در اینجا نیز همان 5 مرحله‌ای را که عنوان کردیم باید جهت تشکیل کلاس جدید ApplicationRoleManager پیگیری کنیم.
1) ارث بری از نگارش جنریک سرویس پایه‌ی موجود و معرفی موجودیت‌های سفارشی سازی شده‌ی خود به آن
public class ApplicationRoleManager :
    RoleManager<Role>,
    IApplicationRoleManager
در اینجا نگارشی از RoleManager را انتخاب کرده‌ایم که بتواند Role سفارشی خود را به سیستم معرفی کند.

2) سفارشی سازی سازنده‌ی این کلاس با سرویسی که تهیه کرده‌ایم (بجای سرویس پیش فرض).
public ApplicationRoleManager(
            IApplicationRoleStore store,
            IEnumerable<IRoleValidator<Role>> roleValidators,
            ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors,
            ILogger<ApplicationRoleManager> logger,
            IHttpContextAccessor contextAccessor,
            IUnitOfWork uow) :
            base((RoleStore<Role, ApplicationDbContext, int, UserRole, RoleClaim>)store, roleValidators, keyNormalizer, errors, logger, contextAccessor)
در این سفارشی سازی دو مورد را تغییر داده‌ایم:
الف) ذکر IApplicationRoleStore بجای RoleStore آن
ب) ذکر IUnitOfWork بجای ApplicationDbContext

3) تکمیل متدهایی که باید به اجبار پس از این ارث بری پیاده سازی شوند.
RoleManager پایه نیازی به پیاده سازی اجباری متدی ندارد.

4) استخراج یک اینترفیس از کلاس نهایی تشکیل شده (توسط ابزارهای Refactoring).
محتوای این اینترفیس را در IApplicationRoleManager‌ مشاهده می‌کنید.

5) معرفی اینترفیس و همچنین نمونه‌ی توکار این سرویس به سیستم تزریق وابستگی‌های ASP.NET Core جهت استفاده‌ی از این سرویس جدید سفارشی سازی شده.
services.AddScoped<IApplicationRoleManager, ApplicationRoleManager>();
services.AddScoped<RoleManager<Role>, ApplicationRoleManager>();
در کلاس IdentityServicesRegistry، یکبار اینترفیس و یکبار اصل سرویس توکار RoleManager را به سرویس جدید و سفارشی سازی شده‌ی ApplicationRoleManager خود هدایت کرده‌ایم.


سفارشی سازی UserStore

در مورد مدیریت کاربران نیز دو سرویس Store و Manager را مشاهده می‌کنید. کار کلاس Store، کپسوله سازی لایه‌ی دسترسی به داده‌ها است تا کتابخانه‌های ثالث، مانند وابستگی Microsoft.AspNetCore.Identity.EntityFrameworkCore بتوانند آن‌را پیاده سازی کنند و کار کلاس Manager، استفاده‌ی از این Store است جهت کار با بانک اطلاعاتی.

5 مرحله‌ای را که باید جهت تشکیل کلاس جدید ApplicationUserStore پیگیری کنیم، به شرح زیر هستند:
1) ارث بری از نگارش جنریک سرویس پایه‌ی موجود و معرفی موجودیت‌های سفارشی سازی شده‌ی خود به آن
public class ApplicationUserStore :
   UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>,
   IApplicationUserStore
از بین نگارش‌های مختلف UserStore، نگارشی را انتخاب کرده‌ایم که بتوان در آن موجودیت‌های سفارشی سازی شده‌ی خود را معرفی کنیم.

2) سفارشی سازی سازنده‌ی این کلاس با سرویسی که تهیه کرده‌ایم (بجای سرویس پیش فرض).
public ApplicationUserStore(
   IUnitOfWork uow,
   IdentityErrorDescriber describer = null)
   : base((ApplicationDbContext)uow, describer)
در این سفارشی سازی یک مورد را تغییر داده‌ایم:
الف) ذکر IUnitOfWork بجای ApplicationDbContext

3) تکمیل متدهایی که باید به اجبار پس از این ارث بری پیاده سازی شوند.
در اینجا نیز تکمیل 4 متد از کلاس پایه UserStore جنریک انتخاب شده، جهت مشخص سازی نحوه‌ی انتخاب کلیدهای خارجی جداول سفارشی سازی شده‌ی مرتبط با جدول کاربران ضروری است:
        protected override UserClaim CreateUserClaim(User user, Claim claim)
        {
            var userClaim = new UserClaim { UserId = user.Id };
            userClaim.InitializeFromClaim(claim);
            return userClaim;
        }

        protected override UserLogin CreateUserLogin(User user, UserLoginInfo login)
        {
            return new UserLogin
            {
                UserId = user.Id,
                ProviderKey = login.ProviderKey,
                LoginProvider = login.LoginProvider,
                ProviderDisplayName = login.ProviderDisplayName
            };
        }

        protected override UserRole CreateUserRole(User user, Role role)
        {
            return new UserRole
            {
                UserId = user.Id,
                RoleId = role.Id
            };
        }

        protected override UserToken CreateUserToken(User user, string loginProvider, string name, string value)
        {
            return new UserToken
            {
                UserId = user.Id,
                LoginProvider = loginProvider,
                Name = name,
                Value = value
            };
        }
در نگارش 2.0 آن، این new‌ها به صورت خودکار توسط خود فریم ورک صورت خواهد گرفت و سفارشی کردن آن ساده‌تر می‌شود.

4) استخراج یک اینترفیس از کلاس نهایی تشکیل شده (توسط ابزارهای Refactoring).
محتوای این اینترفیس را در IApplicationUserStore‌ مشاهده می‌کنید.

5) معرفی اینترفیس و همچنین نمونه‌ی توکار این سرویس به سیستم تزریق وابستگی‌های ASP.NET Core جهت استفاده‌ی از این سرویس جدید سفارشی سازی شده.
services.AddScoped<IApplicationUserStore, ApplicationUserStore>();
services.AddScoped<UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>, ApplicationUserStore>();
در کلاس IdentityServicesRegistry، یکبار اینترفیس و یکبار اصل سرویس توکار UserStore را به سرویس جدید و سفارشی سازی شده‌ی ApplicationUserStore خود هدایت کرده‌ایم.


سفارشی سازی UserManager

5 مرحله‌ای را که باید جهت تشکیل کلاس جدید ApplicationUserManager پیگیری کنیم، به شرح زیر هستند:
1) ارث بری از نگارش جنریک سرویس پایه‌ی موجود و معرفی موجودیت‌های سفارشی سازی شده‌ی خود به آن
public class ApplicationUserManager :
   UserManager<User>,
   IApplicationUserManager
از بین نگارش‌های مختلف UserManager، نگارشی را انتخاب کرده‌ایم که بتوان در آن موجودیت‌های سفارشی سازی شده‌ی خود را معرفی کنیم.

2) سفارشی سازی سازنده‌ی این کلاس با سرویسی که تهیه کرده‌ایم (بجای سرویس پیش فرض).
public ApplicationUserManager(
            IApplicationUserStore store,
            IOptions<IdentityOptions> optionsAccessor,
            IPasswordHasher<User> passwordHasher,
            IEnumerable<IUserValidator<User>> userValidators,
            IEnumerable<IPasswordValidator<User>> passwordValidators,
            ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors,
            IServiceProvider services,
            ILogger<ApplicationUserManager> logger,
            IHttpContextAccessor contextAccessor,
            IUnitOfWork uow,
            IUsedPasswordsService usedPasswordsService)
            : base((UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>)store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
در این سفارشی سازی چند مورد را تغییر داده‌ایم:
الف) ذکر IUnitOfWork بجای ApplicationDbContext (البته این مورد، یک پارامتر اضافی است که بر اساس نیاز این سرویس سفارشی، اضافه شده‌است)
تمام پارامترهای پس از logger به دلیل نیاز این سرویس اضافه شده‌اند و جزو پارامترهای سازنده‌ی کلاس پایه نیستند.
ب) استفاده‌ی از IApplicationUserStore بجای UserStore پیش‌فرض

3) تکمیل متدهایی که باید به اجبار پس از این ارث بری پیاده سازی شوند.
UserManager پایه نیازی به پیاده سازی اجباری متدی ندارد.

4) استخراج یک اینترفیس از کلاس نهایی تشکیل شده (توسط ابزارهای Refactoring).
محتوای این اینترفیس را در IApplicationUserManager‌ مشاهده می‌کنید.

5) معرفی اینترفیس و همچنین نمونه‌ی توکار این سرویس به سیستم تزریق وابستگی‌های ASP.NET Core جهت استفاده‌ی از این سرویس جدید سفارشی سازی شده.
services.AddScoped<IApplicationUserManager, ApplicationUserManager>();
services.AddScoped<UserManager<User>, ApplicationUserManager>();
در کلاس IdentityServicesRegistry، یکبار اینترفیس و یکبار اصل سرویس توکار UserManager را به سرویس جدید و سفارشی سازی شده‌ی ApplicationUserManager خود هدایت کرده‌ایم.


سفارشی سازی SignInManager

سرویس پایه SignInManager از سرویس UserManager جهت فراهم آوردن زیرساخت لاگین کاربران استفاده می‌کند.
5 مرحله‌ای را که باید جهت تشکیل کلاس جدید ApplicationSignInManager پیگیری کنیم، به شرح زیر هستند:
1) ارث بری از نگارش جنریک سرویس پایه‌ی موجود و معرفی موجودیت‌های سفارشی سازی شده‌ی خود به آن
public class ApplicationSignInManager :
    SignInManager<User>,
    IApplicationSignInManager
از بین نگارش‌های مختلف SignInManager، نگارشی را انتخاب کرده‌ایم که بتوان در آن موجودیت‌های سفارشی سازی شده‌ی خود را معرفی کنیم.

2) سفارشی سازی سازنده‌ی این کلاس با سرویسی که تهیه کرده‌ایم (بجای سرویس پیش فرض).
public ApplicationSignInManager(
            IApplicationUserManager userManager,
            IHttpContextAccessor contextAccessor,
            IUserClaimsPrincipalFactory<User> claimsFactory,
            IOptions<IdentityOptions> optionsAccessor,
            ILogger<ApplicationSignInManager> logger)
            : base((UserManager<User>)userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
در این سفارشی سازی یک مورد را تغییر داده‌ایم:
الف) استفاده‌ی از IApplicationUserManager بجای UserManager پیش‌فرض

3) تکمیل متدهایی که باید به اجبار پس از این ارث بری پیاده سازی شوند.
SignInManager پایه نیازی به پیاده سازی اجباری متدی ندارد.

4) استخراج یک اینترفیس از کلاس نهایی تشکیل شده (توسط ابزارهای Refactoring).
محتوای این اینترفیس را در IApplicationSignInManager‌ مشاهده می‌کنید.

5) معرفی اینترفیس و همچنین نمونه‌ی توکار این سرویس به سیستم تزریق وابستگی‌های ASP.NET Core جهت استفاده‌ی از این سرویس جدید سفارشی سازی شده.
services.AddScoped<IApplicationSignInManager, ApplicationSignInManager>();
services.AddScoped<SignInManager<User>, ApplicationSignInManager>();
در کلاس IdentityServicesRegistry، یکبار اینترفیس و یکبار اصل سرویس توکار مدیریت لاگین را به سرویس جدید و سفارشی سازی شده‌ی ApplicationSignInManager خود هدایت کرده‌ایم.


معرفی نهایی سرویس‌های سفارشی سازی شده به ASP.NET Identity Core

تا اینجا سرویس‌های پایه‌ی این فریم ورک را جهت معرفی موجودیت‌های سفارشی سازی شده‌ی خود سفارشی سازی کردیم و همچنین آن‌ها را به سیستم تزریق وابستگی‌های ASP.NET Core نیز معرفی نمودیم. مرحله‌ی آخر، ثبت این سرویس‌ها در رجیستری ASP.NET Core Identity است:
 services.AddIdentity<User, Role>(identityOptions =>
{
}).AddUserStore<ApplicationUserStore>()
  .AddUserManager<ApplicationUserManager>()
  .AddRoleStore<ApplicationRoleStore>()
  .AddRoleManager<ApplicationRoleManager>()
  .AddSignInManager<ApplicationSignInManager>()
  // You **cannot** use .AddEntityFrameworkStores() when you customize everything
  //.AddEntityFrameworkStores<ApplicationDbContext, int>()
  .AddDefaultTokenProviders();
اگر منابع را مطالعه کنید، تمام آن‌ها از AddEntityFrameworkStores و سپس معرفی ApplicationDbContext به آن استفاده می‌کنند. با توجه به اینکه ما همه چیز را در اینجا سفارشی سازی کرده‌ایم، فراخوانی متد افزودن سرویس‌های EF این فریم ورک، تمام آن‌ها را بازنویسی کرده و به حالت اول و پیش فرض آن بر می‌گرداند. بنابراین نباید از آن استفاده شود.
در اینجا متد AddIdentity یک سری  تنظیم‌های پیش فرض‌ها این فریم ورک مانند اندازه‌ی طول کلمه‌ی عبور، نام کوکی و غیره را در اختیار ما قرار می‌دهد به همراه ثبت تعدادی سرویس پایه مانند نرمال ساز نام‌ها و ایمیل‌ها. سپس توسط متدهای AddUserStore، AddUserManager و ... ایی که مشاهده می‌کنید، سبب بازنویسی سرویس‌های پیش فرض این فریم ورک به سرویس‌های سفارشی خود خواهیم شد.

در این مرحله‌است که اگر Migration را اجرا کنید، کار می‌کند و خطای تبدیل نشدن کلاس‌ها به یکدیگر را دریافت نخواهید کرد.


تشکیل مرحله استفاده‌ی از ASP.NET Core Identity و ثبت اولین کاربر در بانک اطلاعاتی به صورت خودکار

روال متداول کار با امکانات کتابخانه‌های نوشته شده‌ی برای ASP.NET Core، ثبت سرویس‌های پایه‌ی آن‌ها توسط متدهای Add است که نمونه‌ی services.AddIdentity فوق دقیقا همین کار را انجام می‌دهد. مرحله‌ی بعد به app.UseIdentity می‌رسیم که کار ثبت میان‌افزارهای این فریم ورک را انجام می‌دهد. متد UseCustomIdentityServices کلاس IdentityServicesRegistry این‌کار را انجام می‌دهد که از آن در کلاس آغازین برنامه استفاده شده‌است.
        public static void UseCustomIdentityServices(this IApplicationBuilder app)
        {
            app.UseIdentity();

            var identityDbInitialize = app.ApplicationServices.GetService<IIdentityDbInitializer>();
            identityDbInitialize.Initialize();
            identityDbInitialize.SeedData();
        }
در اینجا یک مرحله‌ی استفاده‌ی از سرویس IIdentityDbInitializer را نیز مشاهده می‌کنید. کلاس IdentityDbInitializer‌ این اهداف را برآورده می‌کند:
الف) متد Initialize آن، متد context.Database.Migrate را فراخوانی می‌کند. به همین جهت دیگر نیاز به اعمال دستی حاصل Migrations، به بانک اطلاعاتی نخواهد بود. متد Database.Migrate هر مرحله‌ی اعمال نشده‌ای را که باقی مانده باشد، به صورت خودکار اعمال می‌کند.
ب) متد SeedData آن، به نحو صحیحی یک Scope جدید را ایجاد کرده و توسط آن به ApplicationDbContext دسترسی پیدا می‌کند تا نقش Admin و کاربر Admin را به سیستم اضافه کند. همچنین به کاربر Admin، نقش Admin را نیز انتساب می‌دهد. تنظیمات این کاربران را نیز از فایل appsettings.json و مدخل AdminUserSeed آن دریافت می‌کند.


کدهای کامل این سری را در مخزن کد DNT Identity می‌توانید ملاحظه کنید.
  • #
    ‫۶ سال و ۷ ماه قبل، پنجشنبه ۱۹ بهمن ۱۳۹۶، ساعت ۰۳:۱۱
    سلام؛ در پروژه ای که لینکش رو انتهای مطلب آوردین، در ویوی _Layout این تکه کد موجوده
    if (User.IsInRole(ConstantRoles.Admin))
    من بعد از عملیات لاگین وقتی چک میکنه که آیا کاربر وجود داره یا نه از این کد استفاده کردم تا بعد از لاگین کاربر‌ها با نقش‌های مختلف رو به اکشن‌های مربوطه هدایت کنم. اما User من خالیه. این User کجای برنامه پر میشه؟
    • #
      ‫۶ سال و ۷ ماه قبل، پنجشنبه ۱۹ بهمن ۱۳۹۶، ساعت ۰۳:۳۱
      این this.User دقیقا پس از اولین درخواست رسیده‌ی بعدی، با خواندن کوکی تنظیم شده‌ی توسط عملیات لاگین، به صورت خودکار مقدار دهی می‌شود؛ نه پیش از آن (این نحوه‌ی طراحی سیستم Identity است؛ نگارش 2x آن هم به همین صورت است). درست چند سطر بعد از عملیات لاگین، هنوز این کوکی تنظیم شده، مجددا خوانده نشده‌است (چون هنوز درخواست فعلی تکمیل نشده و درخواست جدیدی ارسال نشده‌است). بنابراین دراینجا در صورت نیاز، فقط باید بر اساس مقادیر دریافتی از کاربر، از بانک اطلاعاتی جهت یافتن اطلاعات تکمیلی او کوئری گرفت.
  • #
    ‫۵ سال و ۲ ماه قبل، شنبه ۱ تیر ۱۳۹۸، ساعت ۰۵:۴۴
    سلام.امکانش هست این پروژه روی mysql اجرا کنم ؟ چه جوری؟ چه تغییراتی لازم هست؟ 
  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۱۴ مرداد ۱۳۹۸، ساعت ۲۲:۴۸
    در اینترفیس IApplicationRoleManager متدی با امضاء :
     Task<PagedUsersListViewModel> GetPagedApplicationUsersInRoleListAsync
    تعریف شده است و همچنین در کلاس ApplicationRoleManager این متد بصورت زیر پیاده سازی شده است :
    public async Task<PagedUsersListViewModel> GetPagedApplicationUsersInRoleListAsync
    و برنامه خطای زمان کامپایل زیر رو صادر می‌کنه :
    does not implement interface member 'IApplicationRoleManager.GetPagedApplicationUsersInRoleListAsync
    با توجه به اینکه در بدنه پیاده سازی شده تابع await استفاده شده امکان حذف عبارت async وجود ندارد.این مورد رو چگونه می‌تواند حل کرد؟
    • #
      ‫۵ سال و ۱ ماه قبل، دوشنبه ۱۴ مرداد ۱۳۹۸، ساعت ۲۳:۴۵
      اگر قسمت «برای اجرای این پروژه» را که اخیرا اضافه شده، مطالعه کنید، مشکلی با اجرای این پروژه نخواهید داشت. آخرین سورس موجود در مخزن کد، کاملا آزمایش شده؛ بدون مشکل کامپایل می‌شود و همچنین قابل اجرا است:

  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۲۱ مرداد ۱۳۹۸، ساعت ۱۹:۵۱
    مقدار identityDbInitializer در کد زیر null هست و برنامه با خطای 500.30 متوقف می‌شود. حدس می‌زنید اشکال از کجاست؟
            public static void InitializeDb(this IServiceProvider serviceProvider)
            {
                var scopeFactory = serviceProvider.GetRequiredService<IServiceScopeFactory>();
                using (var scope = scopeFactory.CreateScope())
                {
                    var identityDbInitializer = scope.ServiceProvider.GetService<IIdentityDbInitializer>();
                    identityDbInitializer.Initialize();
                    identityDbInitializer.SeedData();
                }
            }

  • #
    ‫۵ سال و ۱ ماه قبل، چهارشنبه ۲۳ مرداد ۱۳۹۸، ساعت ۱۶:۱۵
    ارتقاء به ASP.NET Core Identity 3.0  

    امضای سازنده‌ی تعدادی از سرویس‌های توکار ASP.NET Core Identity 3.0 تغییر کرده و شامل پارامترهای جدیدی به صورت زیر هستند:
        public class ApplicationSignInManager :
            SignInManager<User>,
            IApplicationSignInManager
        {
       // ...
    
            public ApplicationSignInManager(
                IApplicationUserManager userManager,
                IHttpContextAccessor contextAccessor,
                IUserClaimsPrincipalFactory<User> claimsFactory,
                IOptions<IdentityOptions> optionsAccessor,
                ILogger<ApplicationSignInManager> logger,
                IAuthenticationSchemeProvider schemes,
                IUserConfirmation<User> confirmation)
                : base((UserManager<User>)userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
            {

        public class ApplicationUserManager :
            UserManager<User>,
            IApplicationUserManager
        {
       // ...
    
            public ApplicationUserManager(
                IApplicationUserStore store,
                IOptions<IdentityOptions> optionsAccessor,
                IPasswordHasher<User> passwordHasher,
                IEnumerable<IUserValidator<User>> userValidators,
                IEnumerable<IPasswordValidator<User>> passwordValidators,
                ILookupNormalizer keyNormalizer,
                IdentityErrorDescriber errors,
                IServiceProvider services,
                ILogger<ApplicationUserManager> logger,
                IHttpContextAccessor contextAccessor,
                IUnitOfWork uow,
                IUsedPasswordsService usedPasswordsService)
                : base(
                    (UserStore<User, Role, ApplicationDbContext, int, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>)store,
                      optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
            {

        public class ConfirmEmailDataProtectorTokenProvider<TUser> : DataProtectorTokenProvider<TUser> where TUser : class
        {
            public ConfirmEmailDataProtectorTokenProvider(
                IDataProtectionProvider dataProtectionProvider,
                IOptions<ConfirmEmailDataProtectionTokenProviderOptions> options,
                ILogger<DataProtectorTokenProvider<TUser>> logger)
                : base(dataProtectionProvider, options, logger)
            {

        public class CustomSecurityStampValidator : SecurityStampValidator<User>
        {
       // ...
       
            public CustomSecurityStampValidator(
                IOptions<SecurityStampValidatorOptions> options,
                IApplicationSignInManager signInManager,
                ISystemClock clock,
                ISiteStatService siteStatService,
                ILoggerFactory logger)
                : base(options, (SignInManager<User>)signInManager, clock, logger)
            {
  • #
    ‫۴ سال و ۸ ماه قبل، شنبه ۱۴ دی ۱۳۹۸، ساعت ۲۳:۱۵
    سلام؛ اگر بخواهیم دوتا پروژه درکنارهم داشته باشیم، مثلا یک webapp و یک webapi که مثلا در webapi، فقط سرویس‌ها رو فراخوانی کنیم چه اعمالی نیازبه انجام است.

    • #
      ‫۴ سال و ۸ ماه قبل، شنبه ۱۴ دی ۱۳۹۸، ساعت ۲۳:۲۷
      در ASP.NET Core، برخلاف نگارش‌های قبلی ASP.NET که ASP.NET Web API مجزای از ASP.NET MVC و همچنین وب فرم‌ها ارائه شده بود، اکنون جزئی از ASP.NET MVC است و با آن یکپارچه می‌باشد. اطلاعات بیشتر
      یعنی پروژه را ناقص نکنید. سرویس‌های آن‌را حذف نکنید. تمامش با هم هست و پایه کدهای آن یکی است. همچنین سعی در بازسازی مجدد این پروژه را هم نکنید؛ چندین ماه وقت صرف شده برای یافتن نکات و برقراری اتصالات آن.
      • #
        ‫۴ سال و ۸ ماه قبل، یکشنبه ۱۵ دی ۱۳۹۸، ساعت ۰۰:۵۸
        ممنون. هدف مورد انتظار این هستش که قسمت ادمین رو بجای قراردادن دریک area میتوان در یک webapp دیگه قرار داد ولی سرویس‌های webapp اصلی را به این webapp دوم متصل کرد؟
  • #
    ‫۴ سال و ۴ ماه قبل، دوشنبه ۲۲ اردیبهشت ۱۳۹۹، ساعت ۰۲:۱۸
    با سلام؛ سوالی در مورد این جمله داشتم "وجود Storeها از این جهت است که اگر علاقمند بودید، بتوانید از سایر ORMها و یا زیرساخت‌های ذخیره سازی اطلاعات برای کار با بانک‌های اطلاعاتی استفاده کنید. در اینجا از همان پیاده سازی پیش‌فرض آن که مبتنی بر EF Core است استفاده می‌کنیم. به همین جهت است که وابستگی ذیل را در فایل  project.json  مشاهده می‌کنید: "
    1- میشه بیشتر در مورد store توضیح بدید؟ مثلا اگه بخوام از یک orm دیگه استفاده کنم، وجود store چطور کمک میکنه؟ فلسفه استفاده از store رو متوجه نشدم. فایل project.json هم در پروژه نبود. در داخل مطلب هم باز نشد.
    • #
      ‫۴ سال و ۴ ماه قبل، دوشنبه ۲۲ اردیبهشت ۱۳۹۹، ساعت ۰۴:۰۰
      - این پروژه برای سازگاری با آخرین نگارش موجود، بارها به روز رسانی شده. متن مطلب فوق را تغییر ندادم، ولی کدهای مخزن کد آن کاملا به روز هست.
      - برای درک این مورد باید ساختار پروژه‌ی اصلی Identity را بررسی کنید. در یک طرف آن تعدادی کلاس سطح بالا و abstraction هست و در طرف دیگر پوشه‌ای به نام EntityFrameworkCore که پیاده سازی مخصوص EF-Core این abstraction‌ها است. هستند پروژه‌های دیگری که بجای EntityFrameworkCore از NHibernate و یا MongoDB استفاده کرده باشند.
  • #
    ‫۴ سال و ۳ ماه قبل، چهارشنبه ۳۱ اردیبهشت ۱۳۹۹، ساعت ۲۳:۳۶
    با سپاس فراوان، آیا امکان ویرایش کد های  خود کلاس signInManager , usermanger وجود دارد ؟
    • #
      ‫۴ سال و ۳ ماه قبل، پنجشنبه ۱ خرداد ۱۳۹۹، ساعت ۰۱:۵۸
      بله. نمونه‌‌های سفارشی سازی شده (^ و ^) از یکسری کلاس پایه ارث بری می‌کنند که متدهای virtual آن‌ها قابلیت override و بازنویسی را دارند.
  • #
    ‫۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۰:۲۲
    من موقعی که میخوام کلاس‌ها رو Extract کنم که واسط رو بسازم. کلی پراپرتی و متود دیگه اضافه میشه که میام و با سورس کد شما مطابقت میدم و اضافه هارو برمیدارم، میشه یک توضیحی در مورد اون موارد اضافی بدید مثل پراپرتی‌ها و متود‌های پیشفرض مثل ThrowIfDisposed.
    و اینکه بعضی از متود هارو داخل خود کلاس پیاده سازی نوشتید مثل 
    Task<PasswordVerificationResult> IApplicationUserManager.VerifyPasswordAsync(IUserPasswordStore<User> store, User user, string password)
    {
        return base.VerifyPasswordAsync(store, user, password);
    }
    • #
      ‫۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۱:۴۲
      Identity به همراه اینترفیس برای سرویس‌ها و کلاس‌های اصلی آن نیست و «... از این جهت که در سایر لایه‌های برنامه نمی‌خواهیم از تزریق مستقیم کلاس‌ها استفاده کنیم؛ بلکه می‌خواهیم اینترفیس ها را در موارد ضروری، به سازنده‌های کلاس‌های تزریق نمائیم ...» به همین جهت متدهای مورد نیاز را استخراج و استفاده می‌کنیم.
      • #
        ‫۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۲:۰۴
        در مورد اینکه چرا بعضی از پیاده سازی‌ها در خود کلاس‌ها هستند هم توضیح میدید که چرا به صورت explicit پیاده سازی شده اند.
        Task<PasswordVerificationResult> IApplicationUserManager.VerifyPasswordAsync(IUserPasswordStore<User> store, User user, string password)
        {
            return base.VerifyPasswordAsync(store, user, password);
        }

        • #
          ‫۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۳:۵۵
          چون متد VerifyPasswordAsync به صورت protected تعریف شده و public نیست. روش عمومی کردن آن با همان نام بکار رفته‌ی در کلاس پایه، به صورتی است که مشاهده می‌کنید.
  • #
    ‫۲ سال و ۱۱ ماه قبل، جمعه ۹ مهر ۱۴۰۰، ساعت ۱۸:۳۲
    سلام وقتتون بخیر؛
    دو تا سوال داشتم ممنون میشم جواب بدید.
    من از resharper استفاده میکنم، موقعی که اکسترکت اینترفیس میزنم کامنت‌های متود‌ها و مقادیر پیشفرض پارامتر‌های ورودی متود‌ها رو برام Generate نمیکنه ممنون میشم بگید شما از چه ابزاری برای اکسترکت اینترفیس استفاده کرده اید که هم کامنت‌های متود‌ها و هم مقادیر پیشفرض پارامتر‌های ورودی متود‌ها رو براتون Generate میکنه.
    • #
      ‫۲ سال و ۱۱ ماه قبل، جمعه ۹ مهر ۱۴۰۰، ساعت ۱۹:۵۸
      من از VSCode به همراه Roslynator استفاده می‌کنم.
  • #
    ‫۲ سال و ۷ ماه قبل، چهارشنبه ۶ بهمن ۱۴۰۰، ساعت ۲۰:۵۶
    سلام و خسته نباشید
    من برای fetch کردن دیتا ، دیتای ثبت شده توسط هر یوزر را در کارتابل همان یوزر و به ترتیب تاریخ ثبت آن نمایش میدهم.با توجه به ارث بری کلاس‌ها از IAuditableEntity  به این اطلاعات (CreatedByUserId, CreatedDateTime  ) دسترسی ندارم.چطور در کوئری fetch به پراپرتی های Shadow Properties دسترسی داشته باشم؟

    • #
      ‫۲ سال و ۷ ماه قبل، چهارشنبه ۶ بهمن ۱۴۰۰، ساعت ۲۳:۴۷
      اگر در داخل موجودیتون، همیشه به یکی از پراپرتی‌های موجود در Shadow properties نیاز دارید برای مثال به CreatedDateTime، همون پراپرتی (ها) رو در داخل موجودیت تعریف کنید و به راحتی در هر کوئری به اون دسترسی پیدا کنید.
      public class Category : IAuditableEntity
      {
          public int Id { get; set; }
      
          public Category()
          {
              Products = new HashSet<Product>();
          }
              
          public DateTime? CreatedDateTime { get; set; } //Here
      
          public string Name { get; set; }
      
          public string Title { get; set; }
      
          public virtual ICollection<Product> Products { get; set; }
      }
      اما اگر فقط یکبار به اون نیاز دارید، از متود های GetShadowPropertyValue و نوع جنریک اون استفاده کنید.