اشتراکها
اشتراکها
ASP.NET Core 2.0 منتشر شد
The ASP.NET team is proud to announce general availability of ASP.NET Core 2.0. This release features compatibility with .NET Core 2.0, tooling support in Visual Studio 2017 version 15.3, and the new Razor Pages user-interface design paradigm. For a full list of updates, you can read the release notes. The latest SDK and tools can be downloaded from https://dot.net/core. Read the .NET Core 2.0 release announcement for more information and watch the launch video on Channel 9.
عموما زمانیکه میخواهیم تمام وظایف مدنظر، به صورت موازی اجرا شوند، آنها را Task.WhenAll میکنیم. برای مثال 10 هزار درخواست HTTP را به صورت وظایفی، WhenAll میکنیم و ... در این حالت ... سرور ریموت، IP شما را خواهد بست! چون کنترلی بر روی تعداد وظیفهی در حالت اجرای موازی وجود ندارد و یک چنین عملی، شبیه به یک حملهی DDOS عمل میکند! برای مدیریت بهتر یک چنین مواردی، در دات نت 6 متدهای Parallel.ForEachAsync ارائه شدهاند تا دیگر نیازی به استفاده از راهحلهای ثالثی که عموما آنچنان بهینه هم نیستند، نباشد.
این مجموعه متدها از ValueTaskها بجای Taskها استفاده میکند تا سربار ایجاد Taskها در حلقهها کاهش یابد. همچنین در اینجا degree of parallelism به صورت پیشفرض به تعداد هستههای سیپی تنظیم شدهاست (Environment.ProcessorCount)؛ چون عموما توسعه دهندهها نمیدانند که چه عددی را باید برای آن انتخاب کنند. هر چند امکان تنظیم دستی آنها هم وجود دارد (یکی از مهمترین مشکلات کار با WhenAll).
یک مثال: در اینجا میخواهیم به صورت موازی، مشخصات کاربرانی از Github را توسط HttpClient دریافت کنیم. هر بار هم فقط میخواهیم سه وظیفه اجرا شوند و نه بیشتر
در این مثال، نمونهای از کارکرد متد جدید Parallel.ForEachAsync را مشاهده میکنید که اینبار، MaxDegreeOfParallelism آن قابل تنظیم است. یعنی با تنظیم فوق، هربار فقط سه وظیفه به صورت موازی اجرا خواهند شد. البته تنظیم آن به منهای یک، همان حالت WhenAll را سبب خواهد شد؛ یعنی محدودیتی وجود نخواهد داشت.
متد Parallel.ForEachAsync، آرایهای را که باید بر روی آن کار کند، دریافت میکند. سپس تنظیمات اجرای موازی آنها را هم مشخص میکنیم. در ادامه آنها را در دستههای مشخصی، به صورت موازی بر اساس منطقی که مشخص میکنیم، اجرا خواهد کرد.
وضعیت امکان اجرای موازی متدهای async همزمان، تا پیش از دات نت 6
<List<T به همراه متد الحاقی ForEach است که میتواند یک <Action<T را بر روی المانهای این لیست، اجرا کند و ... عموما زمانیکه به وظایف async میرسیم، به اشتباه مورد استفاده قرار میگیرد:
مثال فوق، با اجرای حلقهی زیر تفاوتی ندارد:
یعنی یک عملیات async، بدون await فراخوانی شدهاست و تا پایان عملیات مدنظر، صبر نخواهد شد. حداقل مشکل آن این است که اگر در این بین استثنایی رخ دهد، هیچگاه متوجه آن نخواهید شد و حتی میتواند کل پروسهی برنامه را خاتمه دهد. شاید عنوان کنید که میشود این مشکل را به صورت زیر حل کرد:
اما ... این روش هم تفاوتی با قبل ندارد. از این لحاظ که متد ForEach یک <Action<T را دریافت میکند که خروجی آن void است. یعنی در نهایت با راه حل دوم، فقط یک async void ایجاد میشود که باز هم قابلیت صبر کردن تا پایان عملیات را ندارد. نکتهی مهم اینجا است که اجرای موازی آنها توسط متد Parallel.ForEach نیز دقیقا همین مشکل را دارد.
تنها راه حل پذیرفتهی شدهی چنین عمل async ای، فراخوانی آنها به صورت متداول زیر و بدون استفاده از متد ForEach است:
و یا Task.WhenAll کردن آنها، با علم به این موضوع که MaxDegreeOfParallelism آن قابل کنترل نیست (حداقل به صورت استاندارد و بدون نیاز به کتابخانههای جانبی). برای مثال بجای نوشتن:
میتوان آنرا به صورت زیر درآورد:
در این حالت عملیات ProcessOrderAsync را تبدیل به لیستی از وظایف مدنظر کرده و به متد Task.WhenAll ارسال میکنیم تا به صورت موازی اجرا شوند. اما ... اگر 10 هزار Task وجود داشته باشند، کنترلی بر روی تعداد وظایف در حال اجرای موازی وجود نخواهد داشت و این مورد نه تنها سبب بالا رفتن کارآیی نخواهد شد، بلکه میتواند سرور را هم با اخلال پردازشی، به علت کمبود منابع در دسترس مواجه کند.
دات نت 6، هم کنترل MaxDegreeOfParallelism را میسر کردهاست و هم اینکه اینبار نگارش async واقعی Parallel.ForEachAsync را ارائه دادهاست تا دیگر همانند حالت قبلی Parallel.ForEach، به async voidها و مشکلات مرتبط با آنها نرسیم.
public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body) public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body) public static Task ForEachAsync<TSource>(IEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body) public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, Func<TSource, CancellationToken, ValueTask> body) public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken, Func<TSource, CancellationToken, ValueTask> body) public static Task ForEachAsync<TSource>(IAsyncEnumerable<TSource> source, ParallelOptions parallelOptions, Func<TSource, CancellationToken, ValueTask> body)
یک مثال: در اینجا میخواهیم به صورت موازی، مشخصات کاربرانی از Github را توسط HttpClient دریافت کنیم. هر بار هم فقط میخواهیم سه وظیفه اجرا شوند و نه بیشتر
using System.Net.Http.Headers; using System.Net.Http.Json; var userHandlers = new [] { "users/VahidN", "users/shanselman", "users/jaredpar", "users/davidfowl" }; using HttpClient client = new() { BaseAddress = new Uri("https://api.github.com"), }; client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DotNet", "6")); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = 3 }; await Parallel.ForEachAsync(userHandlers, parallelOptions, async (uri, token) => { var user = await client.GetFromJsonAsync<GitHubUser>(uri, token); Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n"); }); public class GitHubUser { public string Name { get; set; } public string Bio { get; set; } }
متد Parallel.ForEachAsync، آرایهای را که باید بر روی آن کار کند، دریافت میکند. سپس تنظیمات اجرای موازی آنها را هم مشخص میکنیم. در ادامه آنها را در دستههای مشخصی، به صورت موازی بر اساس منطقی که مشخص میکنیم، اجرا خواهد کرد.
وضعیت امکان اجرای موازی متدهای async همزمان، تا پیش از دات نت 6
<List<T به همراه متد الحاقی ForEach است که میتواند یک <Action<T را بر روی المانهای این لیست، اجرا کند و ... عموما زمانیکه به وظایف async میرسیم، به اشتباه مورد استفاده قرار میگیرد:
customers.ForEach(c => SendEmailAsync(c));
foreach(var c in customers) { SendEmailAsync(c); // the return task is ignored }
customers.ForEach(async c => await SendEmailAsync(c));
تنها راه حل پذیرفتهی شدهی چنین عمل async ای، فراخوانی آنها به صورت متداول زیر و بدون استفاده از متد ForEach است:
foreach(var c in customers) { await SendEmailAsync(c); }
foreach(var o in orders) { await ProcessOrderAsync(o); }
var tasks = orders.Select(o => ProcessOrderAsync(o)).ToList(); await Task.WhenAll(tasks);
دات نت 6، هم کنترل MaxDegreeOfParallelism را میسر کردهاست و هم اینکه اینبار نگارش async واقعی Parallel.ForEachAsync را ارائه دادهاست تا دیگر همانند حالت قبلی Parallel.ForEach، به async voidها و مشکلات مرتبط با آنها نرسیم.
نظرات مطالب
آشنایی با Refactoring - قسمت 9
برای اینکه احتمالا ASP.NET Webforms page life cycle رو رعایت نکردید و الان ViewState صفحه چیزی از وجود کنترلهای پویای شما نمیدونه. مثلا میتونید از DynamicControlsPlaceholder استفاده کنید. اگر جزئیات بیشتری نیاز داشتید این مطالب مفید هستند:
How To Perpetuate Dynamic Controls Between Page Views in ASP.NET
Dynamic Web Controls, Postbacks, and View State
Creating Dynamic Data Entry User Interfaces
ASP.Net Dynamic Controls (Part 1)
ASP.Net Dynamic Controls (Part 2)
ASP.Net Dynamic Controls (Part 3)
ASP.Net Dynamic Controls (Part 4)
How To Perpetuate Dynamic Controls Between Page Views in ASP.NET
Dynamic Web Controls, Postbacks, and View State
Creating Dynamic Data Entry User Interfaces
ASP.Net Dynamic Controls (Part 1)
ASP.Net Dynamic Controls (Part 2)
ASP.Net Dynamic Controls (Part 3)
ASP.Net Dynamic Controls (Part 4)
تغییرات مطابق موارد زیر داده شد ولی باز هم همان ارور
public class CustomNormalizer : ILookupNormalizer { public string NormalizeName(string key) { key = Normalize(key); key = key.ApplyCorrectYeKe() .RemoveDiacritics() .CleanUnderLines() .RemovePunctuation(); key = key.Trim().Replace(" ", ""); return key; } public string NormalizeEmail(string key) { key = Normalize(key); key = fixGmailDots(key); return key; } public string Normalize(string key) { if (string.IsNullOrWhiteSpace(key)) { return null; } key = key.Trim(); key = key.ToUpperInvariant(); return key; } private static string fixGmailDots(string email) { email = email.ToLowerInvariant().Trim(); var emailParts = email.Split('@'); var name = emailParts[0].Replace(".", string.Empty); var plusIndex = name.IndexOf("+", StringComparison.OrdinalIgnoreCase); if (plusIndex != -1) { name = name.Substring(0, plusIndex); } var emailDomain = emailParts[1]; emailDomain = emailDomain.Replace("googlemail.com", "gmail.com"); string[] domainsAllowedDots = { "gmail.com", "facebook.com" }; var isFromDomainsAllowedDots = domainsAllowedDots.Any(domain => emailDomain.Equals(domain)); return !isFromDomainsAllowedDots ? email : string.Format("{0}@{1}", name, emailDomain); } }
اینترفیس ILookupNormalizer به شکل زیر است
#region Assembly Microsoft.Extensions.Identity.Core, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 // C:\Users\kazemi\.nuget\packages\microsoft.extensions.identity.core\3.0.0-preview-18579-0056\lib\netstandard2.0\Microsoft.Extensions.Identity.Core.dll #endregion namespace Microsoft.AspNetCore.Identity { // // Summary: // Provides an abstraction for normalizing keys for lookup purposes. public interface ILookupNormalizer { // // Summary: // Returns a normalized representation of the specified key. // // Parameters: // key: // The key to normalize. // // Returns: // A normalized representation of the specified key. string Normalize(string key); } }
- خیر. یافتن یک کاربر بر اساس ID آن که کلید اصلی جدول را هم تشکیل میدهد و بر روی آن ایندکس وجود دارد، فوق العاده سریع است. یک چنین کوئری گرفتنی در ASP.NET Core Identity هم وجود دارد. البته ASP.NET Core Identity برای انجام اینکار، بیش از یک کوئری را صادر میکند.
- وجود ICookieValidatorService که سبب بروز این کوئری اضافه میشود، در جهت پیاده سازی logout آنی و بررسی به روز بودن محتوای کوکی دریافتی، ضروری هست. نیاز هست تا بتوان مقدار public string SerialNumber مطلب جاری را بین اطلاعات موجود در سرور و اطلاعات قبلی موجود در کوکی صادر شدهی در حین لاگین، مقایسه کرد. اگر این بررسی صورت نگیرد، تا زمانیکه طول عمر کوکی کاربر به پایان نرسیده، با موفقیت و بدون هیچ کوئری اضافهای، اطلاعات موجود در آن، در سمت سرور اعتبارسنجی شده و استفاده میشود. از آن اطلاعات در جهت تامین خودکار user claims و roles کاربر جاری استفاده میشود (همان شیء this.User قابل دسترسی در اکشن متدها)؛ چون زیر ساخت ASP.NET Core در جهت بررسی اطلاعات کوکی رسیده، به همراه دریافت اطلاعات مجدد آن از بانک اطلاعاتی سمت سرور نیست. اگر کوکی را معتبر تشخیص دهد و هنوز طول عمر آن به پایان نرسیده باشد، از اطلاعات آن در جهت ساخت شیء this.User استفاده میکند. اینجا است که اهمیت وجودی ICookieValidatorService در جهت تامین یک لایهی اضافی امنیتی بررسی محتوای کوکی دریافتی، مشخص میشود. برای مثال فرض کنید که کاربری را به دلیل خاصی غیرفعال یا ممنوع الورود کردهاید. اگر سرویس ICookieValidatorService را نداشته باشید، این کاربر تا زمانیکه طول عمر کوکی آن معتبر تشخیص داده شود، میتواند در سایت خراب کاری کند؛ چون اطلاعات کوکی او، شیء this.User معتبری را تولید میکند که وابستهی به اطلاعات جدید بانک اطلاعاتی برنامه نیست. همچنین قرار دادن دستی بررسی دوبارهی اعتبار user claim حاصل از اطلاعات کوکی کاربر جاری در هر اکشن متد و مقایسهی آن با اطلاعات جدید بانک اطلاعاتی، سبب شلوغ شدن و تکرار کدهای اضافهای میشود که به صورت متمرکز و یکجا در ICookieValidatorService وجود دارد. نمونهی دیگر کاربرد این سرویس، پیاده سازی خودکار مفهوم single sign out است؛ به این معنا که اگر کاربری logout کرد، بتوان با تغییر SerialNumber او در سمت سرور، سایر افرادی را که در همان لحظه از اکانت او (کوکیهای معتبر او) در سیستمهای دیگری استفاده میکنند، logout کرد.
- استفاده از کش، به همراه بحث invalidation دقیق آن هم در صورت بروز تغییرات در بانک اطلاعاتی هست. هستند کتابخانههایی که اینکارها را به صورت خودکار و بدون نیازی به تغییری در کدها برای شما انجام دهند.
اشتراکها
مستندات ASP.NET 5
Getting Started Installing ASP.NET 5 On Windows Installing ASP.NET 5 On Mac OS X Installing ASP.NET 5 On Linux Choosing the Right .NET For You on the Server Tutorials Your First ASP.NET 5 Application Using Visual Studio Your First ASP.NET 5 Application on a Mac Publish to an Azure Web App using Visual Studio Conceptual Overview Introduction to ASP.NET 5 Introducing .NET Core DNX Overview Understanding ASP.NET 5 Web Apps Fundamentals Working with Static Files Routing Configuration Dependency Injection Diagnostics Working with Multiple Environments OWIN .NET Execution Environment (DNX) DNX Overview Creating a Cross-Platform Console App with DNX Working with DNX Projects Using Commands Publishing and Deployment Publish to a Docker Image Client-Side Development Grunt and Gulp: Task Runners Manage Client-Side Packages with Bower Building Beautiful, Responsive Sites with Bootstrap Knockout.js MVVM Framework Styling Applications with Less, Sass, and Font Awesome Security Enabling authenication using external providers Account Confirmation and Password Recovery with ASP.NET Identity Two-factor authenication with SMS using ASP.NET Identity Data Protection Extensibility Writing Middleware