مطالب
ارتقاء به NH 3.2 - قسمت دوم

پیشتر مطلبی را در مورد 18 مقاله‌ای که اکثر حالت‌های Mapping موجود در NHibernate را خلاصه کرده بود، مطالعه کردید.
یک مورد هم در این مطلب به نظر در مقایسه با Fluent NHibernate درنظر گرفته نشده است و آن هم بحث AutoMapping است. Fluent NHibernate این قابلیت را دارد که بر اساس تعاریف کلاس‌های شما و روابط بین آن‌ها به صورت خودکار نگاشت‌ها را تشکیل دهید. یعنی خودش مباحث ارتباط‌های یک به چند و چند به چند و غیره را در پشت صحنه به صورت خودکار تولید کند؛ بدون حتی یک سطر کدنویسی اضافی. فقط حداکثر یک سری IAutoMappingOverride و همچنین تعدادی Convention نامگذاری را هم می‌توان جهت تنظیمات این سیستم تمام خودکار اعمال کرد. مثلا توسط IAutoMappingOverride، یکی از خاصیت‌های کلاس را به صورت Unique معرفی کرد و مابقی هم به صورت خودکار توسط قابلیت Autommaping نگاشت می‌شوند. یا توسط Convention نامگذاری سفارشی خود، به Fluent NHibernate اعلام می‌کنیم که من علاقمندم نام مثلا کلیدهای خارجی تشکیل شده بجای اعدادی منحصربفرد، از روش ویژه‌ای که تعیین می‌کنم، ساخته شوند. اینجا است که به نظر من کار با NHibernate حتی از Entity framework هم ساده‌تر است (یا ساده‌تر شده است).
قابلیت AutoMapping یاد شده، در سیستم جدید توکار Mapping by code هم وجود دارد. فقط چون جایی به صورت درست و درمان مستند نشده، همه دور خودشان می‌چرخند! به همین جهت مثالی را در این زمینه آماده کردم که موارد زیر را پوشش می‌دهد:
- نحوه اعمال تنظیمات بانک اطلاعاتی با کدنویسی در NH3,2
- نحوه یکپارچه سازی این روش جدید با کتابخانه NHibernate Validator
- استفاده از NHibernate Validator جهت تنظیم طول فیلدهای بانک اطلاعاتی تولیدی به صورت خودکار
- نحوه تعریف قراردادهای نامگذاری ویژه (مثلا نام جداول تولید شده به صورت خودکار،‌ برخلاف نام موجودیت‌ها، جمع باشد نه مفرد)
- اضافه کردن قابلیت تشخیص many-to-many به auto-mapping موجود در NH3,2 (برای تشخیص این مورد به کمک امکانات مهیای پیش فرض NH3,2، باید اندکی کدنویسی کرد)
- نحوه بازنویسی قراردادهای پیش فرض auto-mapping. مثلا اگر قرار باشد همه چیز خودکار شود اما یکی از فیلدها به صورت unique معرفی شود چکار باید کرد.
- نحوه ذخیره اطلاعات mapping کامپایل شده در فایل فایل و سپس بارگذاری خودکار آن در حین اجرای برنامه جهت بالا بردن سرعت بارگذاری اولیه برنامه.



مطالب
آشنایی با Refactoring - قسمت 7


یکی دیگر از روش‌های Refactoring ، معرفی کردن یک کلاس بجای پارامترها است. عموما تعریف متدهایی با بیش از 5 پارامتر مزموم است:

using System;
using System.Collections.Generic;

namespace Refactoring.Day7.IntroduceParameterObject.Before
{
public class Registration
{
public void Create(string name, DateTime date, DateTime validUntil,
IEnumerable<string> courses, decimal credits)
{
// do work
}
}
}

در این حالت بجای تعریف این تعداد بالای پارامترهای مورد نیاز، تمام آن‌ها را تبدیل به یک کلاس کرده و استفاده می‌کنند:

using System;
using System.Collections.Generic;

namespace Refactoring.Day7.IntroduceParameterObject.After
{
public class RegistrationContext
{
public string Name {set;get;}
public DateTime Date {set;get;}
public DateTime ValidUntil {set;get;}
public IEnumerable<string> Courses {set;get;}
public decimal Credits { set; get; }
}
}

namespace Refactoring.Day7.IntroduceParameterObject.After
{
public class Registration
{
public void Create(RegistrationContext registrationContext)
{
// do work
}
}
}

یکی از مزایای این روش، منعطف شدن معرفی متدها است؛ به این صورت که اگر نیاز به افزودن پارامتر دیگری باشد، تنها کافی است یک خاصیت جدید به کلاس RegistrationContext اضافه شود و امضای متد Create،‌ ثابت باقی خواهد ماند.

روش دیگر تشخیص نیاز به این نوع Refactoring ، یافتن پارامترهایی هستند که در یک گروه قرار می‌گیرند. برای مثال:

public int GetIndex(int pageSize, int pageNumber, ...) { ...

همانطور که ملاحظه می‌کنید تعدادی از پارامترها در اینجا با کلمه page شروع شده‌اند. بهتر است این‌ پارامترهای مرتبط را به یک کلاس مجزا به نام Page انتقال داد.


مطالب
ظهور میکرو ORMs

پس از "معرفی Microsoft.Data.dll یا WebMatrix.Data.dll" که یک کتابخانه‌ی سورس بسته و همچنین مخصوص وب ماتریکس می‌باشد، این ایده توسط سایر برنامه نویس‌ها دنبال و تبدیل به ORMs جدیدی با کمتر از 400 سطر کد شده است که به Micro ORMs هم شهرت یافته‌اند.
در اینجا شما هنوز هم کاملا با SQL سر و کار دارید اما با امکان استفاده بسیار ساده‌تر از پارامترها و همچنین بکارگیری قابلیت‌های جدید dynamic معرفی شده در دات نت 4 . برای مثال:

Dapper
var guid = Guid.NewGuid();
var customer = connection.ExecuteMapperQuery<customer>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

Massive
var tbl = new Products();
var products = tbl.All(where: "CategoryID = @0 AND UnitPrice &gt; @1", orderBy: "ProductName", limit: 20, args: 5,20);

Massive توسط آقای راب کانری که قبلا ORM دیگری را به نام ساب سونیک ایجاد کرده بود، تهیه شده و Dapper توسط تیم سایت StackOverflow جهت مواردی خاصی که استفاده از ORMs (از LINQ to SQL استفاده می‌کنند) هزینه زیادی داشته، مورد استفاده قرار می‌گیرد. در همان صفحه اصلی پروژه، یک سری آمار و ارقام از دید مقایسه کارآیی با سایر ORMs نیز ذکر شده‌اند.
حتی اگر قصد استفاده از آن‌ها را هم نداشته باشید مطالعه کد آن‌ها از دیدگاه کاربردهای عملی قابلیت‌های پویای زبان، بسیار آموزنده هستند.

مطالب
یافتن نام رشته‌ای کامل یک کلاس در دات نت

دو تنظیم زیر را در نظر بگیرید:
<add key="nhibernate-logger" value="NHibernate.Helper.Logging.LoggerFactory, NHibernate.Helper" />
و یا
<add name="StaticContentCacheModule" type="StaticContentCacheModule.StaticCache, StaticContentCacheModule"/>
این نوع موارد را در فایل‌های app.config و یا web.config زیاد می‌توان یافت.
الان فرض کنید کلاس StaticCache مربوط به StaticContentCacheModule فرضی فوق را به صورت دستی به برنامه‌ی خود اضافه کرده‌اید. همچنین سطر فوق را نیز بدون هیچ تغییری در قسمت http modules مربوط به web.config برنامه معرفی نموده‌اید. برنامه را اجرا می‌کنید، اما ماژول ذکر شده کار نمی‌کند! چرا؟
چون نام رشته‌ای متناظر با کلاس StaticCache ایی که اکنون به پروژه‌ی خود اضافه کرده‌اید، با توجه به فضاهای نام پروژه‌ی جدید، کاملا دگرگون شده است. بنابراین، سؤال مهم اینجا است که این نام را بر اساس تنظیمات پروژه‌ی جاری چگونه می‌توان یافت؟
خوشبختانه دات نت فریم ورک، ابزاری توکار را برای تولید این نام رشته‌ای، به همراه دارد:
class Test
{
static void Main()
{
string name = typeof(System.Data.DataView).AssemblyQualifiedName;
Console.WriteLine(name);
}
}
خاصیت AssemblyQualifiedName ذکر شده در مثال فوق، دقیق‌ترین نامی است که می‌توانید در پروژه‌ی خود استفاده نمائید.
خروجی این مثال جهت نمایش نام رشته‌ای معادل کلاس System.Data.DataView به صورت زیر است:
System.Data.DataView, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

سؤال: از کجا متوجه شوم که رشته‌ی فوق واقعا کار می‌کند؟
مقدار متغیر name مثال فوق باید پس از بکارگیری در متد Type.GetType ، حاصلی غیر null را بازگشت دهد.
var name = typeof(System.Data.DataView).AssemblyQualifiedName;
var type = Type.GetType(name);
اگر حاصل نال بود، یعنی همان مشکلی که در ابتدای مطلب ذکر شد: ماژول مشخص شده در web.config برنامه توسط رشته‌ی مورد نظر کار نخواهد کرد.

نکته: اگر قصد معرفی اسمبلی دیگری را به برنامه دارید و این اسمبلی امضای دیجیتال دارد (strong name signature)، باید تمام اطلاعات حاصل را ذکر کنید (مانند مثال فوق که شامل Version ، Public key token و غیره است). در غیر اینصورت (عدم وجود امضای دیجیتال) ذکر دو قسمت اول خروجی خاصیت AssemblyQualifiedName کافی خواهند بود.

مطالب
مدیریت ساده‌تر امور Async (غیر همزمان) در نسخه‌ی بعدی زبان‌های دات نتی

چندی قبل مطلبی را در این سایت در مورد معرفی الگویی که توسط آن می‌توان اعمال غیر همزمان را به صورت پی در پی انجام داد، مطالعه کردید:

و بحث اصلی مطالب فوق هم این است:
"در برنامه نویسی متداول همیشه عادت داریم که اعمال به صورت A –> B –> C انجام شوند. اما در Async programming ممکن است ابتدا C انجام شود، سپس A و بعد B یا هر حالت دیگری صرفنظر از تقدم و تاخر آن‌ها در حین معرفی متدهای مرتبط در یک قطعه کد. همچنین میزان خوانایی این نوع کدنویسی نیز مطلوب نیست...."

خبر خوش آن است که پشتیبانی از این نوع مدل پی در پی برنامه نویسی در نگارش‌های بعدی سی شارپ و VB.NET اضافه شده است.



لیستی از مقالات منتشر شده در این مورد را در ادامه ملاحظه خواهید کرد:

علاوه بر آن یک سری ویدیوی مرتبط با این بحث نیز منتشر شده است:

مطالب
سخت افزارهای قدیمی و ویندوز 7

یک مودم d-link قدیمی external دارم (DFM-560E) که آخرین درایور آن مربوط به ویندوز XP، آن هم 32 بیتی است.
با نصب ویندوز 7 64 بیتی این مودم به صورت یک analog modem غیرقابل استفاده در device manager شناسایی شد. هر چند ویندوز 7 توانایی یافتن آخرین درایورهای مرتبط را از اینترنت و سایت مایکروسافت دارد اما چون مطابق جستجویی که انجام دادم این نوع مودم d-link صرفا برای خارج از آمریکا تهیه و تولید شده، دیگر درایوری برای آن تهیه نخواهد شد. خوب، خدا را شکر!
این مودم، مستعمل ماند تا اینکه در یکی از سایت‌ها، مقاله‌ی زیر را دیدم:


این مقاله در مورد همان گزینه‌ی add new hardware ویندوزهای قبلی که در اینجا به نام add legacy hardware تغییر نام یافته، صحبت می‌کند.
در device manager ویندوز 7 از منوی Actions گزینه‌ی add legacy hardware را انتخاب کنید تا همان ویزاد آشنای add new hardware ویندوز XP ظاهر شود. تمام مراحل آن هم مانند قبل است.
در مرحله‌ی have disk مربوط به مودم، فایل زیر را که مربوط به معرفی درایور مودم ویندوز 64 بیتی است به آن معرفی کردم و مشکل حل شد (احتمالا با تمام مودم‌هایی با chipset از نوع Conexant باید کار کند).
دریافت فایل




الان هنوز همان analog modem شناسایی نشده باقی است اما مودم فوق نیز شناسایی شد و بدون مشکل کار می‌کند.



هنگام ساخت کانکشن دایال آپ هم این مودم جدید شناسایی شده و قابل استفاده است.




مطالب
خواندنی‌های 2 مرداد

اس کیوال سرور

الگوهای طراحی برنامه نویسی شیءگرا

امنیت

توسعه وب

دات نت فریم ورک

دبلیو اف

سی و مشتقات

کتاب‌های رایگان

لینوکس

متفرقه

محیط‌های مجتمع توسعه

مسایل انسانی، اجتماعی و مدیریتی برنامه نویسی

ویندوز

مطالب
لیست تازه‌های IIS 7.5

IIS 7.5 که به همراه ویندوز سرور 2008 R2 ارائه می‌شود شامل تازه‌های زیر است:

  • بیش از 50 مورد cmdlet جدید مخصوص Powershell جهت مدیریت IIS
  • افزونه‌های جدید مدیریتی: Database Manager (مدیریت اس کیوال سرور از درون IIS و کنسول آن)، Configuration Editor (تولید خودکار اسکریپت‌های مدیریتی جهت اتوماسیون امور مرتبط)، IIS Reports و Request Filtering .
  • پشتیبانی از One-click publishing موجود در Visual Studio 10
  • Web Deployment Tool یا همان MS Deploy سابق جهت مدیریت بهتر برنامه‌های وب.
  • امکان ردیابی تغییرات در کانفیگ وب سرور
  • گزارشگیری بهتر از وضعیت کارآیی سرور
  • ساپورت دات نت جهت Server Core معرفی شده در ویندوز سرور 2008
  • WebDav که پیشتر به صورت یک افزونه‌ی آن معرفی شده بود، اکنون جزئی از IIS 7.5 است.
  • یکپارچگی با URLScan 3.0 جهت بالا بردن امنیت وب سرور.
  • FTP server services : با کنسول مدیریتی IIS یکپارچه شده است با بهبودهایی در نحوه‌ی تنظیم کردن و ردیابی آن.

جهت مطالعه بیشتر در مورد تازه‌های ویندوز سرور 2008 نگارش R2 می‌توان به مقالات زیر رجوع کرد:
Windows Server 2008 R2 new features - the complete list - Part 1: Virtualization
Windows Server 2008 R2 new features - the complete list - Part 2: Active Directory
Windows Server 2008 R2 new features - the complete list - Part 3: IIS 7.5 and Performance
Windows Server 2008 R2 new features - the complete list - Part 4: Administration

مطالب
سفارشی سازی 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 می‌توانید ملاحظه کنید.
بازخوردهای دوره
تزریق وابستگی‌های AutoMapper در لایه سرویس برنامه
- محل تعریف نگاشت‌ها و کلاس‌های پروفایل، مهم نیست. چون اساسا هرجایی که قرار گیرند، دو وابستگی بیشتر نخواهند داشت: کلاس‌های مدل و کلاس‌های ViewModel.
- محل فراخوانی اولیه‌ی تعاریف نگاشت‌ها جهت معرفی آن‌ها به سیستم، مهم است.
+ اگر از کاربر اطلاعاتی را دریافت می‌کنید، در لایه UI هست که کار نگاشت اطلاعات دریافتی از کاربر و از ViewModelها به Modelهای اصلی برنامه انجام می‌شود (توسط متد Mapper.Map). اگر قرار است اطلاعاتی را بازگشت دهید، متدهای جدیدی مانند Project To بسیار بهینه‌تر هستند از روش قدیمی Mapper.Map و این متد را بهتر است در لایه سرویس استفاده کنید. متد Project To کارش بهینه سازی کوئری SQL ارسالی به سرور هست. اگر از روش Mapper.Map در لایه UI استفاده کنید، این قابلیت را از دست خواهید داد؛ چون Mapper.Map به معنای کار با اشیاء درون حافظه و LINQ to Objects است. کار متد ویژه‌ی Project To افزونه‌ای برای کار با Entity Framework و بهینه سازی آن است.