مطالب
تکمیل کلاس DelegateCommand

مدت‌ها از کلاس DelegateCommand معرفی شده در این آدرس استفاده می‌کردم. این کلاس یک مشکل جزئی دارد و آن هم عدم بررسی مجدد قسمت canExecute به صورت خودکار هست.

خلاصه‌ای برای کسانی که بار اول هست با این مباحث برخورد می‌کنند؛ یا MVVM به زبان بسیار ساده:

در برنامه نویسی متداول سیستم مایکروسافتی، در هر سیستمی که ایجاد کرده و در هر فناوری که ارائه داده از زمان VB6 تا امروز، شما روی یک دکمه مثلا دوبار کلیک می‌کنید و در فایل اصطلاحا code behind این فرم و در روال رخدادگردان آن شروع به کد نویسی خواهید کرد. این مورد تقریبا در همه جا صادق است؛ از WinForms تا WPF تا Silverlight تا حتی ASP.NET Webforms . به عمد هم این طراحی صورت گرفته تا برنامه نویس‌ها در این محیط‌ها زیاد احساس غریبی نکنند. اما این روش یک مشکل مهم دارد و آن هم «توهم» جداسازی رابط کاربر از کدهای برنامه است. به ظاهر یک فایل فرم وجود دارد و یک فایل جدای code behind ؛ اما در عمل هر دوی این‌ها یک partial class یا به عبارتی «یک کلاس» بیشتر نیستند. «فکر می‌کنیم» که از هم جدا شدند اما واقعا یکی هستند. شما در code behind صفحه به صورت مستقیم با عناصر رابط کاربری سروکار دارید و کدهای شما به این عناصر گره خورده‌اند.
شاید بپرسید که چه اهمیتی دارد؟
مشکل اول: امکان نوشتن آزمون‌ها واحد برای این متدها وجود ندارد یا بسیار سخت است. این متدها فقط با وجود فرم و رابط کاربری متناظر با آن‌ها هست که معنا پیدا می‌کنند و تک تک عناصر آن‌ها وهله سازی می‌شوند.
مشکل دوم: کد نوشته فقط برای همین فرم جاری آن قابل استفاده است؛ چون به صورت صریح به عناصر موجود در فرم اشاره می‌کند. نمی‌تونید این فایل code behind رو بردارید بدون هیچ تغییری برای فرم دیگری استفاده کنید.
مشکل سوم: نمی‌تونید طراحی فرم رو بدید به یک نفر، کد نویسی اون رو به شخصی دیگر. چون ایندو لازم و ملزوم یکدیگرند.

این سیستم کد نویسی دهه 90 است.
چند سالی است که طراحان سعی کرده‌اند این سیستم رو دور بزنند و روش‌هایی رو ارائه بدن که در آن‌ها فرم‌های برنامه و فایل‌های پیاده سازی کننده‌ی منطق آن هیچگونه ارتباط مستقیمی باهم نداشته باشند؛ به هم گره نخورده باشند؛ ارجاعی به هیچیک از عناصر بصری فرم را در خود نداشته باشند. به همین دلیل ASP.NET MVC به وجود آمده و در همان سال‌ها مثلا MVVM .

سؤال:
الان که رابط کاربری از فایل پیاده سازی کننده منطق آن جدا شده و دیگر Code behind هم نیست (همان partial class های متداول)، این فایل‌ها چطور متوجه می‌شوند که مثلا روی یک فرم، شیءایی قرار گرفته؟ از کجا متوجه خواهند شد که روی دکمه‌ای کلیک شده؟ این‌ها که ارجاعی از فرم را در درون خود ندارند.
در الگوی MVVM این سیم کشی توسط امکانات قوی Binding موجود در WPF میسر می‌شود. در ASP.NET MVC چیزی شبیه به آن به نام Model binder و همان مکانیزم‌های استاندارد HTTP این کار رو می‌کنه. در MVVM شما بجای code behind خواهید داشت ViewModel (اسم جدید آن). در ASP.NET MVC این اسم شده Controller. بنابراین اگر این اسامی رو شنیدید زیاد تعجب نکنید. این‌ها همان Code behind قدیمی هستند اما ... بدون داشتن ارجاعی از رابط کاربری در خود که ... اطلاعات موجود در فرم به نحوی به آن‌ها Bind و ارسال می‌شوند.
این سیم کشی‌ها هم نامرئی هستند. یعنی فایل ViewModel یا فایل Controller نمی‌دونند که دقیقا از چه کنترلی در چه فرمی این اطلاعات دریافت شده.
این ایده هم جدید نیست. شاید بد نباشه به دوران طلایی Win32 برگردیم. همان توابع معروف PostMessage و SendMessage را به خاطر دارید؟ شما در یک ترد می‌تونید با مثلا PostMessage شیءایی رو به یک فرم که در حال گوش فرا دادن به تغییرات است ارسال کنید (این سیم کشی هم نامرئی است). بنابراین پیاده سازی این الگوها حتی در Win32 و کلیه فریم ورک‌های ساخته شده بر پایه آن‌ها مانند VCL ، VB6 ، WinForms و غیره ... «از روز اول» وجود داشته و می‌تونستند بعد از 10 سال نیان بگن که اون روش‌های RAD ایی رو که ما پیشنهاد دادیم، می‌شد خیلی بهتر از همان ابتدا، طور دیگری پیاده سازی بشه.

ادامه بحث!
این سیم کشی یا اصطلاحا Binding ، در مورد رخدادها هم در WPF وجود داره و اینبار به نام Commands معرفی شده‌است. به این معنا که بجای اینکه بنویسید:
<Button  Click="btnClick_Event">Last</Button>

بنویسید:
<Button Command="{Binding GoLast}">Last</Button>

حالا باید مکانیزمی وجود داشته باشه تا این پیغام رو به ViewModel برنامه برساند. اینکار با پیاده سازی اینترفیس ICommand قابل انجام است که معرفی یک کلاس عمومی از پیاده سازی آن‌را در ابتدای بحث مشاهده نمودید.
در یک DelegateCommand،‌ توسط متد منتسب به executeAction، مشخص خواهیم کرد که اگر این سیم کشی برقرار شد (که ما دقیقا نمی‌دانیم و نمی‌خواهیم که بدانیم از کجا و کدام فرم دقیقا)، لطفا این اعمال را انجام بده و توسط متد منتسب به canExecute به سیستم Binding خواهیم گفت که آیا مجاز هستی این اعمال را انجام دهی یا خیر. اگر این متد false برگرداند، مثلا دکمه یاد شده به صورت خودکار غیرفعال می‌شود.
اما مشکل کلاس DelegateCommand ذکر شده هم دقیقا همینجا است. این دکمه تا ابد غیرفعال خواهد ماند. در WPF کلاسی وجود دارد به نام CommandManager که حاوی متدی استاتیکی است به نام InvalidateRequerySuggested. اگر این متد به صورت دستی فراخوانی شود، یکبار دیگر کلیه متدهای منتسب به تمام canExecute های تعریف شده، به صورت خودکار اجرا می‌شوند و اینجا است که می‌توان دکمه‌ای را که باید مجددا بر اساس شرایط جاری تغییر وضعیت پیدا کند، فعال کرد. بنابراین فراخوانی متد InvalidateRequerySuggested یک راه حل کلی رفع نقیصه‌ی ذکر شده است.
راه حل دومی هم برای حل این مشکل وجود دارد. می‌توان از رخدادگردان CommandManager.RequerySuggested استفاده کرد. روال منتسب به این رخدادگردان هر زمانی که احساس کند تغییری در UI رخ داده، فراخوانی می‌شود. بنابراین پیاده سازی بهبود یافته کلاس DelegateCommand به صورت زیر خواهد بود:

using System;
using System.Windows.Input;

namespace MvvmHelpers
{
// Ref.
// - http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/
// - http://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/
public class DelegateCommand<T> : ICommand
{
readonly Func<T, bool> _canExecute;
bool _canExecuteCache;
readonly Action<T> _executeAction;

public DelegateCommand(Action<T> executeAction, Func<T, bool> canExecute = null)
{
if (executeAction == null)
throw new ArgumentNullException("executeAction");

_executeAction = executeAction;
_canExecute = canExecute;
}

public event EventHandler CanExecuteChanged
{
add { if (_canExecute != null) CommandManager.RequerySuggested += value; }
remove { if (_canExecute != null) CommandManager.RequerySuggested -= value; }
}

public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}

public void Execute(object parameter)
{
_executeAction((T)parameter);
}
}
}

استفاده از آن هم در ViewModel ساده است. یکبار خاصیتی به این نام تعریف می‌شود. سپس در سازنده کلاس مقدار دهی شده و متدهای متناظر آن تعریف خواهند شد:

public DelegateCommand<string> GoLast { set; get; }

//in ctor
GoLast = new DelegateCommand<string>(goLast, canGoLast);

private bool canGoLast(string data)
{
//ex.
return ListViewGuiData.CurrentPage != ListViewGuiData.TotalPage - 1;
}

private void goLast(string data)
{
//do something
}

مزیت کلاس DelegateCommand جدید هم این است که مثلا متد canGoLast فوق، به صورت خودکار با به روز رسانی UI ، فراخوانی و تعیین اعتبار مجدد می‌شود.


نظرات مطالب
مقدمه‌ای بر تزریق وابستگی‌ها درASP.NET Core
موردی که مدنظر شما است تزریق وابستگی‌ها نام ندارد و الگوی service locator است. اگر بخواهید از آن در ASP.NET Core استفاده کنید، نظرات و نکات تکمیلی مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها» را مطالعه کنید و خصوصا عبارات « GetService » و یا « GetRequiredService » را در آن جستجو کنید؛ مانند Request.HttpContext.RequestServices.GetService. 
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 8 - فعال سازی ASP.NET MVC
در ASP.NET Core 3.0، بنابر درخواست استفاده کنندگان که به نظر آن‌ها AddMvc تعداد زیادی از سرویس‌ها را به سیستم اضافه می‌کند که شاید مورد استفاده قرار نگیرند (برای مثال کسانیکه فقط با Web API کار می‌کنند، نیازی به سرویس‌های Viewها ندارند)، متدهای اختصاصی‌تری طراحی شده‌اند. البته AddMvc حذف نخواهد شد و همانند قبل رفتار می‌کند.
هدف
سرویس‌های اضافه شده
متد تنظیم سرویس‌ها
- مناسب برای توسعه‌ی Web API
- اما باید بخاطر داشت که این سرویس‌ها را به صورت پیش‌فرض اضافه نمی‌کند:
  • Antiforgery
  • Temp Data
  • Views
  • Pages
  • Tag Helpers
  • Memory Cache 
  • Controllers
  • Model Binding
  • API Explorer (OpenAPI integration)
  • Authorization [Authorize]
  • CORS [EnableCors]
  • Data Annotations validation [Required]
  • Formatter Mappings (translate a file-extension to a content-type) 
 ()AddControllers 
شبیه به ASP.NET Core 1.X عمل می‌کند؛ یعنی سرویس Pages را که مرتبط با Razor Pages است، به صورت پیش‌فرض ثبت نمی‌کند.
  • Controllers
  • Model Binding
  • API Explorer (OpenAPI integration)
  • Authorization [Authorize]
  • CORS [EnableCors]
  • Data Annotations validation [Required]
  • Formatter Mappings (translate a file-extension to a content-type)
  • Antiforgery
  • Temp Data
  • Views
  • Tag Helpers
  • Memory Cache 
 ()AddControllersWithViews
 برای کار با Razor pages بوده و این سرویس‌ها را به صورت پیش‌فرض به همراه ندارد:
  • API Explorer (OpenAPI integration)
  • CORS [EnableCors]
  • Formatter Mappings (translate a file-extension to a content-type) 
  • Pages
  • Controllers
  • Model Binding
  • Authorization [Authorize]
  • Data Annotations validation [Required]
  • Antiforgery
  • Temp Data
  • Views
  • Tag Helpers
  • Memory Cache 
 ()AddRazorPages
مطالب
چگونه یک ایمیل مفید خودکار را طراحی کنیم؟

از آنجائیکه مدتی قسمتی از کارم مرتبط بود به طراحی ایمیل‌های خودکار برای برنامه‌های تهیه شده (مثلا، ایمیل‌های مرحله به مرحله یک گردش کاری ... اطلاع رسانی‌های خودکار از وضعیت داده‌ها،‌ گزارشاتی از برنامه‌ها که به صورت خبرنامه‌های ایمیلی در بازه‌های زمانی مشخصی به اشخاص مشخص شده ارسال می‌شد و غیره)، لازم می‌دونم خلاصه‌ای از تجربیات برخورد با کاربران را در این مورد در ادامه ذکر کنم، شاید مفید باشد.

1) حتما در انتهای ایمیل خودکار ارسالی، ساعت و تاریخ شمسی ارسال پیام را نیز ذکر کنید.
عموما از آنجائیکه سیستم استاندارد ارسال ایمیل بر اساس تاریخ میلادی است و تقریبا تمام کلاینت‌های دریافت ایمیل موجود نیز توانایی شمسی سازی تاریخ دریافت و ارسال ایمیل را ندارند (مگر با یک سری افزونه و یا دستکاری در سیستم عامل که آنچنان خوشایند و مرسوم نیست)، ذکر تاریخ شمسی در انتهای پیام بسیار مفید خواهد بود و در اکثر اوقات استناد به ایمیل‌های دریافت شده بر اساس تاریخ دریافت آن‌ها است.

2) سعی کنید از بکارگیری عناوین (subject) ثابت جهت ارسال ایمیل‌های خودکار پرهیز کنید.
دقیقا یادم میاد زمانیکه برای مدیر عامل شرکتی سه بار پشت سرهم ایمیلی با یک عنوان ارسال شده بود بنده را بازخواست کردند که چرا برنامه‌ی شما ایمیل تکراری ارسال می‌کند!
بله، سعی می‌کنند محتوا را از روی عنوان ایمیل حدس بزنند و زمانیکه یک عنوان ثابت را برای ایمیل‌های خودکار خود انتخاب کردید، تکراری به نظر خواهند رسید یا حتی ممکن است به اشتباه پیش از خوانده شدن حذف شوند.
برای مثال فرض کنید ایمیل ارجاع کاری را قرار است به صورت خودکار ارسال کنید. انتخاب عنوان ثابت برای مثال "ارجاع کار جدید" اشتباه است! این عنوان باید بر اساس نوع کار هر بار به صورت پویا متغیر باشد؛ مثلا: "ارجاع کار جدید: از طرف : ... ، موضوع: ... ، درجه اهمیت: ..." که این سه نقطه‌ها باید توسط برنامه هر بار پر شوند.

3) هر چه می‌توانید اطلاعات بیشتری را توسط یک ایمیل خودکار منتقل کنید.
مورد قبل را در نظر بگیرید. ذکر "ارجاع کار جدید ..." در عنوان و سپس مجددا ذکر همین عنوان به عنوان بدنه‌ی ایمیل خودکار به زودی ایمیل‌های شما را تبدیل به نوعی Spam آزار دهنده خواهد کرد. کار جدیدی ارجاع شده است؟ آیا می‌توان خلاصه‌ای از این کار را به همراه ایمیل نیز ارسال کرد تا کاربر حتما برای مشاهده‌ی ریز جزئیات کار به برنامه مراجعه نکند و این ایمیل واقعا ارزش مطالعه را داشته باشد و سبب تسریع در انجام کارها شود؟
برای مثال ذکر کلی این مورد که درخواست مرخصی جدیدی را باید تائید یا رد کنید، کافی نیست. ریز جزئیات مرخصی را هم به همراه ایمیل ارسال کنید.

4) ایمیل شما باید حاوی لینکی جهت باز کردن برنامه‌ی تحت وب مرتبط نیز باشد.
کاری ارجاع شده است؟ بهتر است لینک پویایی را جهت هدایت کاربر به صفحه‌ی مرتبط رسیدگی به همان کار ارجاعی ارسال کنید. به این صورت زحمت او را کمتر کرده و یک مرحله گزارش گیری را حذف خواهید کرد. یا حداقل یک محل مراجعه‌ی کلی بعدی را به این صورت می‌توان ارائه داد.

5) از بکارگیری قسمت from ایی مانند DoNotReply@Site.Com خودداری کنید.
کاربر دریافت کننده‌ی ایمیل باید بداند که در صورت وجود مشکل باید به کجا مراجعه کند؟ چه کسی این ایمیل را ارسال کرده؟
هرچند برنامه به صورت خودکار تمام قسمت‌های این ایمیل ارسالی را تهیه می‌کند اما اگر خبرنامه‌ی تنظیم شده‌ای نیست، حتما شخص ارسال کننده‌ای دارد. یا حداقل یک ایمیل عمومی را برای این مورد تنظیم کنید (ایمیلی که وجود خارجی داشته و هر از چندگاهی بررسی می‌شود).

6) رنگ زمینه و اندازه‌ی قلم مناسبی را انتخاب کنید.
دقیقا برای هر کدام از موارد ذکر شده چندین بار مشکل داشته‌ام! عموما کسانی که ایمیل‌ها را دریافت می‌کنند سن و سال دار هستند. بنابراین انتخاب فونت tahoma با اندازه‌ی 8 یا pt 7 سبب توبیخ زود هنگام شما خواهد شد!
همچنین هر چه ساده‌تر بهتر. دقیقا مشکلات از زمانی آغاز می‌شوند که طرحی را انتخاب کنید یا رنگی را برای زمینه بکار ببرید. اینجا است که هر روز یک سلیقه‌ی تحمیلی را باید پذیرا باشید.

7) دقیقا مشخص کنید که ایمیل دریافتی آیا رونوشت‌ است یا خیر!
همان مبحث ارجاع کار را در نظر بگیرید. پس از اینکه سیستم راه اندازی شد، مدیر یکی از قسمت‌ها چند روز بعد این درخواست را "حتما" ارسال خواهد کرد: رونوشت تمام کارهای ارجاعی به کلیه پرسنل بخش و همچنین ریز اقدامات آن‌ها باید برای بنده نیز ارسال شود.
در اینجا تنها افزودن قسمت CC به ایمیل‌های خودکار کفایت نمی‌کند. حتما به صورت درشت در بالای ایمیل، قبل از شروع بدنه ذکر کنید که ایمیل دریافتی یک رونوشت است. در غیر اینصورت باید پاسخگوی علت دریافت ایمیل‌هایی باشید که به درخواست خودشان CC شده است!

8) از ایمیل‌های خودکار برنامه log تهیه کنید.
بارها به این مساله برخورد کرده‌ام که اشخاص برای شانه خالی کردن از انجام کار محوله، سعی در تخریب کار شما خواهند داشت. خیلی ساده عنوان می‌کنند که ایمیلی را دریافت نکرده‌اند. حالا شما بیاید ثابت کنید که اگر سیستم مشکل داشت کلا برای هیچ کسی ایمیل ارسال نمی‌شد، نه فقط برای شما. در اینگونه مواقع وجود یک لاگ از ایمیل‌ها (ثبت در بانک اطلاعاتی) و ارجاع به آن‌ها بسیار راه گشا است.

9) راهی را برای خلاص شدن از شر دریافت ایمیل‌های خودکار نیز پیش بینی کنید!
همان مورد 7 را در نظر بگیرید. دو روز اول خیلی ذوق خواهند کرد! روز سوم وقتی انبوهی از ایمیل‌ها را دریافت کردند، مشکل شما هم شروع خواهد شد. بنابراین امکان تنظیم دریافت یا عدم دریافت ایمیل را حتما در برنامه قرار دهید. یا حداقل نحوه‌ی ایجاد یک پوشه جدید و فیلتر کردن ایمیل‌های رسیده و هدایت خودکار آن‌ها به این پوشه‌ی جدید را آموزش دهید.


خوب! حالا به نظر شما این ایمیل خودکار ارسالی سایت IDevCenter که اخیرا اضافه شده است چه نمره‌ای را کسب می‌کند؟



- تاریخ شمسی در انتهای ایمیل ندارد.
- عنوان‌ها ثابت هستند.
- هیچ جزئیاتی ارائه نشده است.
- لینک مرتبط دارد.
- قسمت from مناسبی دارد.
- ساده است؛ خوب است! فقط اندازه قلم آن بهتر است یک شماره بزرگتر شود.
- بحث رونوشت اینجا مورد ندارد.
- بحث لاگ ... شخصی است.
- امکان تنظیم دریافت ایمیل پیش بینی شده است.
نمره از 7 : 3.5

نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت اول - موجودیت‌های پایه و DbContext برنامه
جهت اطلاع
پروژه‌ی DNTIdentity به ASP.NET Core 2.1 ارتقاء داده شد.
- مشاهده‌ی لیست کامل تغییرات 

برای اجرای آن فقط کافی است:
- ابتدا SDK جدید را نصب کنید.
- سپس مجوز SSL آن‌را تبدیل به مجوز امن و قابل اطمینان کنید.
- در ادامه به پوشه‌ی DataLayer مراجعه کرده و ابتدا دستور dotnet restore را صادر کنید. بعد از آن دو فایل cmd موجود در آن‌را اجرا کنید. فایل اول مهاجرت‌ها را تولید می‌کند و فایل دوم، آن‌ها را به بانک اطلاعاتی از نوع LocalDB اعمال خواهد کرد. بانک اطلاعاتی تولید شده را در پوشه‌ی wwwroot/App_Data می‌توانید مشاهده کنید.
- در آخر به پوشه‌ی اصلی برنامه مراجعه کرده و دو فایل bat موجود در آن‌را اجرا کنید. اولی وابستگی‌ها را بازیابی می‌کند و دومی برنامه را کامپایل کرده و سپس بر روی پورت SSL 5001 ارائه می‌دهد که بلافاصله در مرورگر قابل مشاهده خواهد بود.

برای اجرای این مراحل نیاز به IDE خاصی ندارید. همینقدر که SDK جدید را نصب کرده باشید، کافی است.  
نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت اول - موجودیت‌های پایه و DbContext برنامه
جهت اطلاع
پروژه‌ی DNTIdentity به ASP.NET Core 2.0 ارتقاء داده شد.
- مشاهده‌ی لیست کامل تغییرات

برای اجرای آن فقط کافی است
- ابتدا SDK جدید را نصب کنید.
- سپس به پوشه‌ی DataLayer مراجعه کرده و ابتدا دستور dotnet restore را صادر کنید. بعد از آن دو فایل cmd موجود در آن‌را اجرا کنید. فایل اول مهاجرت‌ها را تولید می‌کند و فایل دوم، آن‌ها را به بانک اطلاعاتی از نوع LocalDB اعمال خواهد کرد. بانک اطلاعاتی تولید شده را در پوشه‌ی wwwroot/App_Data می‌توانید مشاهده کنید.
- در آخر به پوشه‌ی اصلی برنامه مراجعه کرده و دو فایل bat موجود در آن‌را اجرا کنید. اولی وابستگی‌ها را بازیابی می‌کند و دومی برنامه را کامپایل کرده و سپس بر روی پورت 5000 ارائه می‌دهد که بلافاصله در مرورگر قابل مشاهده خواهد بود.

برای اجرای این مراحل نیاز به IDE خاصی ندارید. همینقدر که SDK جدید را نصب کرده باشید، کافی است. 
نظرات مطالب
بررسی بهبودهای ProblemDetails در ASP.NET Core 7x
در ASP.NET Core 8.0 نیز امکان مدیریت استثناءها به صورت سراسری با پیاده‌سازی اینترفیس  IExceptionHandler مسیر شده است:
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

internal sealed class GlobalExceptionHandler : IExceptionHandler
{
    private readonly ILogger<GlobalExceptionHandler> _logger;

    public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
    {
        _logger = logger;
    }

    public async ValueTask<bool> TryHandleAsync(
        HttpContext context,
        Exception exception,
        CancellationToken cancellationToken = default)
    {
        _logger.LogError(exception, "An unhandled exception has occurred while executing the request.");
        var problemDetails = new ProblemDetails
        {
            Status = StatusCodes.Status500InternalServerError,
            Title = "Server Error",
        };

        context.Response.StatusCode = StatusCodes.Status500InternalServerError;
        await context.Response.WriteAsJsonAsync(problemDetails);

        return true;
    }
}

برای ریجستر کردن آن نیز:
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
// other code
app.UseExceptionHandler();
نظرات مطالب
بازنویسی سطح دوم کش برای Entity framework 6
- کش سطح دوم نباید برای کش کردن اطلاعات خصوصی استفاده شود؛ یا کلا اطلاعاتی که نیاز به سطح دسترسی دارند. هدف آن کش کردن اطلاعات عمومی و پر مصرف است. اطلاعات خاص یک کاربر نباید کش شوند.
- در تمام سیستم‌ها، برای مواردی که به کوئری‌های آن دسترسی ندارید تا متد Cacheable را به آن‌ها اضافه کنید، نتیجه‌ی کوئری‌ها را باید خودتان از طریق روش‌های متداول کش کنید (مانند کلاس CacheManager مطلب یاد شده).
مطالب
بررسی تغییرات Blazor 8x - قسمت ششم - نکات تکمیلی ویژگی راهبری بهبود یافته‌ی صفحات SSR


در قسمت قبل، در حین بررسی رفتار جزیره‌های تعاملی Blazor Server، نکته‌ی زیر را هم درباره‌ی راهبری صفحات SSR مرور کردیم:
« اگر دقت کنید، جابجایی بین صفحات، با استفاده از fetch انجام شده؛ یعنی با اینکه این صفحات در اصل static HTML خالص هستند، اما ... کار full reload صفحه مانند ASP.NET Web forms قدیمی انجام نمی‌شود (و یا حتی برنامه‌های MVC و Razor pages) و نمایش صفحات، Ajax ای است و با fetch استاندارد آن صورت می‌گیرد تا هنوز هم حس و حال SPA بودن برنامه حفظ شود. همچنین اطلاعات DOM کل صفحه را هم به‌روز رسانی نمی‌کند؛ فقط موارد تغییر یافته در اینجا به روز رسانی خواهند شد.»
در این قسمت، نکات تکمیلی این قابلیت را که به آن enhanced navigation هم گفته می‌شود، بررسی می‌کنیم.


روش غیرفعال کردن راهبری بهبودیافته برای بعضی از لینک‌ها

ویژگی راهبری بهبودیافته فقط در حین هدایت بین صفحات مختلف یک برنامه‌ی Blazor 8x SSR، فعال است. اگر در این بین، کاربری به یک صفحه‌ی غیر بلیزری هدایت شود، راهبری بهبود یافته شکست خورده و سعی می‌کند حالت full document load را پیاده سازی و اجرا کند. مشکل اینجاست که در این حالت دو درخواست ارسال می‌شود: ابتدا حالت راهبری بهبودیافته فعال می‌شود و در ادامه پس از شکست این راهبری، هدایت مستقیم صورت می‌گیرد. برای رفع این مشکل می‌توان ویژگی جدید data-enhance-nav را با مقدار false، به لینک‌های خارجی مدنظر اضافه کرد تا برای این حالت‌ها دیگر ویژگی راهبری بهبودیافته فعال نشود:
<a href="/not-blazor" data-enhance-nav="false">A non-Blazor page</a>


فعالسازی مدیریت بهبودیافته‌ی فرم‌های SSR

در قسمت چهارم این سری با فرم‌های جدید SSR مخصوص Blazor 8x آشنا شدیم. این فرم‌ها هم می‌توانند از امکانات راهبری بهبود یافته استفاده کنند (یعنی مدیریت ارسال آن، توسط fetch API انجام شده و به روز رسانی قسمت‌های تغییریافته‌ی صفحه را Ajax ای انجام دهند)؛ برای نمونه اینبار همانند تصویر زیر، از fetch استاندارد برای ارسال اطلاعات به سمت سرور کمک گرفته می‌شود (یعنی عملیات Ajax ای شده‌؛ بجای یک post-back معمولی):


 اما ... این قابلیت به صورت پیش‌فرض در فرم‌های تعاملی SSR غیرفعال است. چون همانطور که عنوان شد، اگر مقصد این فرم، یک آدرس غیربلیزری باشد، دوبار ارسال فرم صورت خواهد گرفت؛ یکبار با استفاده از fetch API و بار دیگر پس از شکست، به صورت معمولی. اما اگر مطمئن هستید که endpoint این فرم، قطعا یک کامپوننت بلیزری است، بهتر است این قابلیت را در یک چنین فرم‌هایی نیز به صورت زیر فعال کنید:
<form method="post" @onsubmit="() => submitted = true" @formname="name" data-enhance>
    <AntiforgeryToken />
    <InputText @bind-Value="Name" />
    <button>Submit</button>
</form>

@if (submitted)
{
    <p>Hello @Name!</p>
}

@code {

    bool submitted;

    [SupplyParameterFromForm]
    public string Name { get; set; } = "";
}
البته باتوجه به اینکه در اینجا هم می‌توان از EditForm‌ها استفاده کرد، در این حالت فقط کافی است ویژگی Enhance را به آن‌ها اضافه نمائید:
<EditForm method="post" Model="NewCustomer" OnValidSubmit="() => submitted = true" FormName="customer" Enhance>
    <DataAnnotationsValidator />
    <ValidationSummary/>
    <p>
        <label>
            Name: <InputText @bind-Value="NewCustomer.Name" />
        </label>
    </p>
    <button>Submit</button>
</EditForm>

@if (submitted)
{
    <p id="pass">Hello @NewCustomer.Name!</p>
}

@code {
    bool submitted = false;

    [SupplyParameterFromForm]
    public Customer? NewCustomer { get; set; }

    protected override void OnInitialized()
    {
        NewCustomer ??= new();
    }

     public class Customer
    {
        [StringLength(3, ErrorMessage = "Name is too long")]
        public string? Name { get; set; }
    }
}

نکته‌ی مهم: در این حالت فرض بر این است که هیچگونه هدایتی به یک Non-Blazor endpoint صورت نمی‌گیرید؛ وگرنه با یک خطا مواجه خواهید شد.



غیرفعال کردن راهبری بهبودیافته برای قسمتی از صفحه

اگر با استفاده از جاواسکریپت و خارج از کدهای بیلزر، اطلاعات DOM را به‌روز رسانی می‌کنید، ویژگی راهبری بهبودیافته، از آن آگاهی نداشته و به صورت خودکار تمام تغییرات شما را بازنویسی می‌کند. به همین جهت اگر نیاز است قسمتی از صفحه را که مستقیما توسط کدهای جاواسکریپتی تغییر می‌دهید، از به‌روز رسانی‌های این قابلیت مصون نگه‌دارید، می‌توانید ویژگی جدید data-permanent را به آن قسمت اضافه کنید:
<div data-permanent>
    Leave me alone! I've been modified dynamically.
</div>


امکان آگاه شدن از بروز راهبری بهبودیافته در کدهای جاواسکریپتی

اگر به هردلیلی در کدهای جاواسکریپتی خودنیاز به آگاه شدن از وقوع یک هدایت بهبودیافته را دارید (برای مثال جهت بازنویسی تغییرات ایجاد شده‌ی توسط آن)، می‌توانید به نحو زیر، مشترک رخ‌دادهای آن شوید:
<script>
    Blazor.addEventListener('enhancedload', () => {
        console.log('enhanced load event occurred');
    });
</script>


ویژگی جدید Named Element Routing در Blazor 8x

Blazor 8x از ویژگی مسیریابی سمت کلاینت به کمک تعریف URL fragments پشتیبانی می‌کند. به این صورت رسیدن (اسکرول) به یک قسمت از صفحه‌ای طولانی، بسیار ساده می‌شود.
برای مثال المان h2 با id مساوی targetElement را درنظر بگیرید:
<div class="border border-info rounded bg-info" style="height:500px"></div>

<h2 id="targetElement">Target H2 heading</h2>
<p>Content!</p>
در Blazor 8x برای رسیدن به آن، هر سه حالت زیر میسر هستند:
<a href="/counter#targetElement">

<NavLink href="/counter#targetElement">

Navigation.NavigateTo("/counter#targetElement");


معرفی متد جدید Refresh در Blazor 8x

در Blazor 8x، امکان بارگذاری مجدد صفحه با فراخوانی متد جدید NavigationManager.Refresh(bool forceLoad = false) میسر شده‌است. این متد در حالت پیش‌فرض از قابلیت راهبری بهبودیافته برای به روز رسانی صفحه استفاده می‌کند؛ مگر اینکه اینکار میسر نباشد. اگر آن‌را با پارامتر true فراخوانی کنید، full-page reload رخ خواهد داد.
همین اتفاق در مورد متد Navigation.NavigateTo نیز رخ‌داده‌است. این متد نیز در Blazor 8x به صورت پیش‌فرض بر اساس قابلیت راهبری بهبود یافته کار می‌کند؛ مگر اینکه اینکار میسر نباشد و یا پارامتر forceLoad آن‌را به true مقدار دهی کنید.