اشتراکها
اشتراکها
NET MAUI. چیست؟
اشتراکها
انتشار پیش نمایش Android 12
Refactoring
Improving the Design of Existing Code by Martin Fowler
اشتراکها
سایت JavaScript Garden
Work in progress to add support for covariant return types to the .NET runtime. Soon we'll be able to override a virtual method returning `object` with a method returning `string`. Because of how array variance works, weird things might be possible in IL.
class Base { public virtual IntPtr[] Fun() => null; } // This is obvious pseude-code because C# won't let us introduce methods differing // in return type. C# also requires to be explicit about "virtual" and "override". // But IL... not so much. class Derived : Base { // overrides Base.Fun on 32bit platforms. public override uint[] Fun() => null; // overrides Base.Fun on 64bit platforms. public override ulong[] Fun() => null; }
اشتراکها
بررسی وبسایتهای سازمانی در ایران
چندی قبل مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» را در سایت جاری مطالعه کردید. پیاده سازی یک چنین قابلیتی به صورت توکار در ASP.NET Core Identity پیش بینی شدهاست. همچنین تمام اعتبارسنجهای نامهای کاربران، کلمات عبور آنها، ایمیلهای آنها و غیره را نیز میتوان سفارشی سازی کرد و بجای سرویسهای پیشفرض آنها معرفی و جایگزین نمود.
سفارشی سازی نرمال سازها
اگر به طراحی جداول ASP.NET Core Identity دقت کنید، تعدادی فیلد اضافی حاوی کلمهی Normalized را هم مشاهده خواهید کرد. برای مثال:
در جدول کاربران، فیلدهای Email و UserName به همراه دو فیلد اضافهی NormalizedEmail و NormalizedUserName وجود دارند.
مقدار دهی و مدیریت این فیلدهای ویژه به صورت خودکار توسط کلاسی به نام UpperInvariantLookupNormalizer صورت میگیرد:
این کلاس اینترفیس ILookupNormalizer را پیاده سازی کرده و تنها کاری را که انجام میدهد، تبدیل نام کاربر، نام نقشها و یا ایمیل کاربر به حالت upper case آن است. اما هدف اصلی از آن چیست؟
همانطور که در مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» نیز عنوان شد، برای مثال ایمیلهای جیمیل را میتوان با چندین حالت مختلف ثبت کرد و یک کاربر به این صورت میتواند شرط یکتا بودن آدرس ایمیلهای تنظیم شدهی در کلاس IdentityServicesRegistry را دور بزند:
به همین جهت برای سفارشی سازی آن کلاس CustomNormalizer با سفارشی سازی UpperInvariantLookupNormalizer پیاده سازی شدهاست.
چون تنها یک اینترفیس ILookupNormalizer وجود دارد، باید بر اساس محتوای کلیدی که به آن ارسال میشود:
تصمیمگیری کرد که آیا ایمیل است یا خیر. چون از این نرمال کننده هم برای ایمیلها و هم برای نامها استفاده میشود. سپس میتوان منطقهای سفارشی خود مانند حذف نقطههای اضافی ایمیلها و یا حذف کاراکترهای اضافی اعمالی به نامهای کاربری را اعمال کرد.
پس از تدارک کلاس CustomNormalizer، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomNormalizer را به عنوان پیاده سازی کنندهی ILookupNormalizer معرفی کردهایم. همچنین یکبار هم سرویس توکار UpperInvariantLookupNormalizer را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomNormalizer ما استفاده خواهد شد.
بنابراین دیگر نیازی نیست تا در حین ثبتنام نسبت به تمیزسازی ایمیلها و یا نامهای کاربری اقدام کنیم. سرویس ILookupNormalizer در پشت صحنه به صورت خودکار در تمام مراحل ثبت نام و به روز رسانیها توسط ASP.NET Core Identity استفاده میشود.
سفارشی سازی UserValidator
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کاربران است که با پیاده سازی اینترفیس IUserValidator ارائه شدهاست:
این سرویس پیشفرض و توکار، تنظیمات Options.User.RequireUniqueEmail، Options.User.AllowedUserNameCharacters و امثال آنرا در مورد نامهای کاربری و ایمیلها بررسی میکند (تنظیم شدهی در متد setUserOptions کلاس IdentityServicesRegistry).
بنابراین اگر قصد تهیهی یک IUserValidator جدید را داشته باشیم، از تمام تنظیمات و بررسیهای پیش فرض سرویس توکار UserValidator فوق محروم میشویم. به همین جهت برای سفارشی سازی این سرویس، از خود کلاس UserValidator ارث بری کرده و سپس base.ValidateAsync آنرا فراخوانی میکنیم. با اینکار سبب خواهیم شد تا تمام اعتبارسنجیهای پیشفرض ASP.NET Core Identity اعمال شده و پس از آن منطقهای سفارشی اعتبارسنجی خود را که در کلاس CustomUserValidator قابل مشاهده هستند، اضافه میکنیم.
در اینجا برای مثال در متد validateEmail سفارشی تهیه شده، لیست یک سری fake email provider اضافه شدهاند (مدخل EmailsBanList در فایل appsettings.json برنامه) تا کاربران نتوانند از آنها جهت ثبتنام استفاده کنند و یا در متد validateUserName سفارشی، اگر نام کاربری برای مثال عددی وارد شده بود، یک new IdentityError بازگشت داده میشود.
پس از تدارک کلاس CustomUserValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomUserValidator را به عنوان پیاده سازی کنندهی IUserValidator معرفی کردهایم. همچنین یکبار هم سرویس توکار UserValidator را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomUserValidator ما استفاده خواهد شد (حتی اگر UserValidator اصلی از سیستم تزریق وابستگیها درخواست شود).
سفارشی سازی PasswordValidator
مراحل سفارشی سازی اعتبارسنج کلمات عبور نیز همانند تهیهی CustomUserValidator فوق است.
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کلمات عبور کاربران است که با پیاده سازی اینترفیس IPasswordValidator ارائه شدهاست:
در این کلاس، از اطلاعات متد setPasswordOptions کلاس IdentityServicesRegistry
که از فایل appsettings.json و مدخل PasswordOptions آن تامین میشود:
جهت اعتبارسنجی کلمات عبور وارد شدهی توسط کاربران در حین ثبت نام و یا به روز رسانی اطلاعات خود، استفاده میشود.
بنابراین در اینجا نیز ارائهی یک پیاده سازی خام از IPasswordValidator سبب خواهد شد تا تمام اعتبارسنجیهای توکار کلاس PasswordValidator اصلی را از دست بدهیم. به همین جهت کار را با ارث بری از همین کلاس توکار شروع کرده و ابتدا متد base.ValidateAsync آنرا فراخوانی میکنیم تا مطمئن شویم، مدخل PasswordOptions تنظیمات یاد شده، حتما پردازش خواهند شد. سپس منطق سفارشی خود را اعمال میکنیم.
برای مثال در کلاس CustomPasswordValidator تهیه شده، به مدخل PasswordsBanList فایل appsettings.json مراجعه شده و کاربران را از انتخاب کلمات عبوری به شدت ساده، منع میکند.
پس از تدارک کلاس CustomPasswordValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
یکبار CustomPasswordValidator را به عنوان پیاده سازی کنندهی IPasswordValidator معرفی کردهایم. همچنین یکبار هم سرویس توکار PasswordValidator را به سرویس سفارشی خودمان هدایت کردهایم. به این ترتیب مطمئن خواهیم شد که همواره از CustomPasswordValidator ما استفاده خواهد شد (حتی اگر PasswordValidator اصلی از سیستم تزریق وابستگیها درخواست شود).
پردازش نتایج اعتبارسنجها
این اعتبارسنجها در خروجیهای IdentityResult تمام متدهای ASP.NET Core Identity ظاهر میشوند. بنابراین فراخوانی سادهی UpdateUserAsync اشتباه است و حتما باید خروجی آنرا جهت پردازش IdentityResult آن بررسی کرد. به همین جهت تعدادی متد الحاقی به کلاس IdentityExtensions اضافه شدهاند تا کارکردن با IdentityResult را سادهتر کنند.
متد AddErrorsFromResult خطاهای حاصل از عملیات ASP.NET Core Identity را به ModelState جاری اضافه میکند. به این ترتیب میتوان این خطاها را به کاربر در Viewهای برنامه و در قسمت اعتبارسنجی مدل آن نمایش داد.
و یا متد DumpErrors تمام خطاهای موجود در IdentityResult را تبدیل به یک رشته میکند. برای مثال میتوان این رشته را در Remote validationها مورد استفاده قرار داد.
استفادهی از این متدهای الحاقی را در کنترلرهای برنامه میتوانید مشاهده کنید.
استفادهی از اعتبارسنجها جهت انجام Remote validation
اگر به RegisterController دقت کنید، اکشن متدهای ValidateUsername و ValidatePassword قابل مشاهده هستند:
این اکشن متدها توسط سرویسهای
تزریق شدهی به سازندهی کلاس، پیاده سازی شدهاند. در مورد تامین آنها و سفارشی سازی آنها نیز پیشتر بحث شد. این اینترفیسها دقیقا همان وهلههای CustomUserValidator و CustomPasswordValidator را در اختیار ما قرار میدهند. تنها کاری را که باید انجام دهیم، فراخوانی متد ValidateAsync آنها است. این متد یک خروجی از نوع IdentityResult را دارد. به همین جهت متد DumpErrors را برای پردازش این نتیجه تدارک دیدیم.
به این ترتیب کاربران در حین ثبت نام، راهنمای بهتری را جهت انتخاب کلمات عبور و نام کاربری مشاهده خواهند کرد و این بررسیها نیز Ajax ایی هستند و پیش از ارسال فرم نهایی به سرور اتفاق میافتند.
برای فعالسازی Remote validation، علاوه بر ثبت اسکریپتهای Ajax ایی، خواص کلاس RegisterViewModel نیز از ویژگی Remote استفاده میکنند:
یک نکته: برای اینکه Remote Validation را به همراه ValidateAntiForgeryToken استفاده کنیم، تنها کافی است نام فیلد مخفی آنرا به لیست AdditionalFields به نحوی که مشاهده میکنید، اضافه کنیم.
کدهای کامل این سری را در مخزن کد DNT Identity میتوانید ملاحظه کنید.
سفارشی سازی نرمال سازها
اگر به طراحی جداول ASP.NET Core Identity دقت کنید، تعدادی فیلد اضافی حاوی کلمهی Normalized را هم مشاهده خواهید کرد. برای مثال:
در جدول کاربران، فیلدهای Email و UserName به همراه دو فیلد اضافهی NormalizedEmail و NormalizedUserName وجود دارند.
مقدار دهی و مدیریت این فیلدهای ویژه به صورت خودکار توسط کلاسی به نام UpperInvariantLookupNormalizer صورت میگیرد:
public class UpperInvariantLookupNormalizer : ILookupNormalizer
همانطور که در مطلب «نرمال سازی اطلاعات کاربران در حین ثبت نام» نیز عنوان شد، برای مثال ایمیلهای جیمیل را میتوان با چندین حالت مختلف ثبت کرد و یک کاربر به این صورت میتواند شرط یکتا بودن آدرس ایمیلهای تنظیم شدهی در کلاس IdentityServicesRegistry را دور بزند:
identityOptionsUser.RequireUniqueEmail = true;
چون تنها یک اینترفیس ILookupNormalizer وجود دارد، باید بر اساس محتوای کلیدی که به آن ارسال میشود:
public override string Normalize(string key)
پس از تدارک کلاس CustomNormalizer، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<ILookupNormalizer, CustomNormalizer>(); services.AddScoped<UpperInvariantLookupNormalizer, CustomNormalizer>();
بنابراین دیگر نیازی نیست تا در حین ثبتنام نسبت به تمیزسازی ایمیلها و یا نامهای کاربری اقدام کنیم. سرویس ILookupNormalizer در پشت صحنه به صورت خودکار در تمام مراحل ثبت نام و به روز رسانیها توسط ASP.NET Core Identity استفاده میشود.
سفارشی سازی UserValidator
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کاربران است که با پیاده سازی اینترفیس IUserValidator ارائه شدهاست:
public class UserValidator<TUser> : IUserValidator<TUser> where TUser : class
بنابراین اگر قصد تهیهی یک IUserValidator جدید را داشته باشیم، از تمام تنظیمات و بررسیهای پیش فرض سرویس توکار UserValidator فوق محروم میشویم. به همین جهت برای سفارشی سازی این سرویس، از خود کلاس UserValidator ارث بری کرده و سپس base.ValidateAsync آنرا فراخوانی میکنیم. با اینکار سبب خواهیم شد تا تمام اعتبارسنجیهای پیشفرض ASP.NET Core Identity اعمال شده و پس از آن منطقهای سفارشی اعتبارسنجی خود را که در کلاس CustomUserValidator قابل مشاهده هستند، اضافه میکنیم.
public override async Task<IdentityResult> ValidateAsync(UserManager<User> manager, User user) { // First use the built-in validator var result = await base.ValidateAsync(manager, user).ConfigureAwait(false); var errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList(); // Extending the built-in validator validateEmail(user, errors); validateUserName(user, errors); return !errors.Any() ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray()); }
پس از تدارک کلاس CustomUserValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<IUserValidator<User>, CustomUserValidator>(); services.AddScoped<UserValidator<User>, CustomUserValidator>();
سفارشی سازی PasswordValidator
مراحل سفارشی سازی اعتبارسنج کلمات عبور نیز همانند تهیهی CustomUserValidator فوق است.
ASP.NET Core Identity به همراه یک سرویس توکار اعتبارسنج کلمات عبور کاربران است که با پیاده سازی اینترفیس IPasswordValidator ارائه شدهاست:
public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
private static void setPasswordOptions(PasswordOptions identityOptionsPassword, SiteSettings siteSettings) { identityOptionsPassword.RequireDigit = siteSettings.PasswordOptions.RequireDigit; identityOptionsPassword.RequireLowercase = siteSettings.PasswordOptions.RequireLowercase; identityOptionsPassword.RequireNonAlphanumeric = siteSettings.PasswordOptions.RequireNonAlphanumeric; identityOptionsPassword.RequireUppercase = siteSettings.PasswordOptions.RequireUppercase; identityOptionsPassword.RequiredLength = siteSettings.PasswordOptions.RequiredLength; }
"PasswordOptions": { "RequireDigit": false, "RequiredLength": 6, "RequireLowercase": false, "RequireNonAlphanumeric": false, "RequireUppercase": false },
بنابراین در اینجا نیز ارائهی یک پیاده سازی خام از IPasswordValidator سبب خواهد شد تا تمام اعتبارسنجیهای توکار کلاس PasswordValidator اصلی را از دست بدهیم. به همین جهت کار را با ارث بری از همین کلاس توکار شروع کرده و ابتدا متد base.ValidateAsync آنرا فراخوانی میکنیم تا مطمئن شویم، مدخل PasswordOptions تنظیمات یاد شده، حتما پردازش خواهند شد. سپس منطق سفارشی خود را اعمال میکنیم.
برای مثال در کلاس CustomPasswordValidator تهیه شده، به مدخل PasswordsBanList فایل appsettings.json مراجعه شده و کاربران را از انتخاب کلمات عبوری به شدت ساده، منع میکند.
پس از تدارک کلاس CustomPasswordValidator، تنها کاری را که باید در جهت معرفی و جایگرینی آن انجام داد، تغییر ذیل در کلاس IdentityServicesRegistry است:
services.AddScoped<IPasswordValidator<User>, CustomPasswordValidator>(); services.AddScoped<PasswordValidator<User>, CustomPasswordValidator>();
پردازش نتایج اعتبارسنجها
این اعتبارسنجها در خروجیهای IdentityResult تمام متدهای ASP.NET Core Identity ظاهر میشوند. بنابراین فراخوانی سادهی UpdateUserAsync اشتباه است و حتما باید خروجی آنرا جهت پردازش IdentityResult آن بررسی کرد. به همین جهت تعدادی متد الحاقی به کلاس IdentityExtensions اضافه شدهاند تا کارکردن با IdentityResult را سادهتر کنند.
public static void AddErrorsFromResult(this ModelStateDictionary modelStat, IdentityResult result)
public static string DumpErrors(this IdentityResult result, bool useHtmlNewLine = false)
استفادهی از این متدهای الحاقی را در کنترلرهای برنامه میتوانید مشاهده کنید.
استفادهی از اعتبارسنجها جهت انجام Remote validation
اگر به RegisterController دقت کنید، اکشن متدهای ValidateUsername و ValidatePassword قابل مشاهده هستند:
[AjaxOnly, HttpPost, ValidateAntiForgeryToken] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public async Task<IActionResult> ValidateUsername(string username, string email)
[AjaxOnly, HttpPost, ValidateAntiForgeryToken] [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public async Task<IActionResult> ValidatePassword(string password, string username)
IPasswordValidator<User> passwordValidator, IUserValidator<User> userValidator,
به این ترتیب کاربران در حین ثبت نام، راهنمای بهتری را جهت انتخاب کلمات عبور و نام کاربری مشاهده خواهند کرد و این بررسیها نیز Ajax ایی هستند و پیش از ارسال فرم نهایی به سرور اتفاق میافتند.
برای فعالسازی Remote validation، علاوه بر ثبت اسکریپتهای Ajax ایی، خواص کلاس RegisterViewModel نیز از ویژگی Remote استفاده میکنند:
[Required(ErrorMessage = "(*)")] [Display(Name = "نام کاربری")] [Remote("ValidateUsername", "Register", AdditionalFields = nameof(Email) + "," + ViewModelConstants.AntiForgeryToken, HttpMethod = "POST")] [RegularExpression("^[a-zA-Z_]*$", ErrorMessage = "لطفا تنها از حروف انگلیسی استفاده نمائید")] public string Username { get; set; }
یک نکته: برای اینکه Remote Validation را به همراه ValidateAntiForgeryToken استفاده کنیم، تنها کافی است نام فیلد مخفی آنرا به لیست AdditionalFields به نحوی که مشاهده میکنید، اضافه کنیم.
کدهای کامل این سری را در مخزن کد DNT Identity میتوانید ملاحظه کنید.
نظرات مطالب
EF Code First #4
سلام آقای نصیری؛
منم همین پیام را دریافت میکنم، ولی من EF نسخۀ 5.0 را استفاده میکنم و اونو هم با NuGet به پروژه اضافه کردهم. ویژوال استدیو نسخۀ 2012 هست و با داتنت 4.5 برنامه را ایجاد کردهم. برنامه تا آخر درس قبل کاملاً همونطور که انتظار میرفت اجرا شد و مشکلی هم نداشت.
اما به محض باز کردن package manager console از منوی Tools، بعد از معرفی نسخۀ کنسول
Package Manager Console Host Version 2.6.40627.9000 خطوط زیر را با زمینۀ قرمز مینویسه:
برای دستور enable-migrations هم دقیقاً همون چیزی را مینویسه که در کامنت اول davmszd بیان شده، یعنی اینو:
و هنگامی هم که دستور Get-Packageرا اجرا میکنم، اینو مینویسه:
ضمناً در پوشۀ packages در کنار پروژه، فولدری بنام EntityFramework.5.0.0 و داخل اون هم فولدر tools با این فایلها وجود داره:
ممنون میشم اگر منو راهنمایی بفرمایید.
منم همین پیام را دریافت میکنم، ولی من EF نسخۀ 5.0 را استفاده میکنم و اونو هم با NuGet به پروژه اضافه کردهم. ویژوال استدیو نسخۀ 2012 هست و با داتنت 4.5 برنامه را ایجاد کردهم. برنامه تا آخر درس قبل کاملاً همونطور که انتظار میرفت اجرا شد و مشکلی هم نداشت.
اما به محض باز کردن package manager console از منوی Tools، بعد از معرفی نسخۀ کنسول
Package Manager Console Host Version 2.6.40627.9000 خطوط زیر را با زمینۀ قرمز مینویسه:
Test-ModuleManifest : The specified module 'D:\Entity Framework Samples\EF Sample 02\packages\EntityFramework.5.0.0\tools\EntityFramework.psd1' was not loaded because no valid module file was found in any module directory. At D:\Entity Framework Samples\EF Sample 02\packages\EntityFramework.5.0.0\tools\init.ps1:14 char:34 + $thisModule = Test-ModuleManifest <<<< (Join-Path $toolsPath $thisModuleManifest) + CategoryInfo : ResourceUnavailable: (D:\Entity Framework Samples\E...yFramework.psd1:String) [Test-ModuleManifest], FileNotFoundException + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.TestModuleManifestCommand Import-Module : Cannot bind argument to parameter 'Name' because it is null. At D:\Entity Framework Samples\EF Sample 02\packages\EntityFramework.5.0.0\tools\init.ps1:31 char:18 + Import-Module <<<< $thisModule + CategoryInfo : InvalidData: (:) [Import-Module], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ImportModuleCommand
The term 'enable-migrations' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:18 + enable-migrations <<<< + CategoryInfo : ObjectNotFound: (enable-migrations:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException
Id Version Description/Release Notes -- ------- ------------------------- EntityFramework 5.0.0 Entity Framework is Microsoft's recommended data access technology for new applications.
about_EntityFramework.help.txt EntityFramework.PowerShell.dll EntityFramework.PowerShell.Utility.dll EntityFramework.PS3.psd1 EntityFramework.psd1 EntityFramework.psm1 init.ps1 install.ps1 migrate.exe Redirect.config Redirect.VS11.config