آیا قصد مهاجرت به NET Core. را دارید؟
در حال بررسی هستم
خیر
معرفی پروژه DNTFrameworkCore
پروژه DNTFrameworkCore که قصد پشتیبانی از آن را دارم، یک زیرساخت سبک وزن و توسعه پذیر با پشتیبانی از طراحی چند مستاجری با کمترین وابستگی به کتابخانههای ثالث میباشد که با تمرکز بر کاهش زمان و افزایش کیفیت توسعه بخش منطق تجاری پروژههای تحت وب، توسعه داده شده است. به مرور زمان مطالب و مستندات آن نیز کامل خواهد شد. برای برخی از امکانات از جمله اعتبارسنجی خودکار، مدیریت تراکنش ها، شماره گذاری خودکار و ... آزمون واحد نیز در نظر گرفته شده است که در آینده نزدیک با تکمیل آزمون واحد بخشهای دیگر، انتشار آنها نیز انجام خواهد شد.
برای نصب و استفاده از بستههای نیوگت آن، دستورات زیر را اجرا کنید:
PM>Install-Package DNTFrameworkCore PM>Install-Package DNTFrameworkCore.EntityFramework PM>Install-Package DNTFrameworkCore.Web PM>Install-Package DNTFrameworkCore.Web.EntityFramework
به منظور بررسی دقیقتر امکانات آن میتوانید پروژه TestAPI موجود در مخزن گیت هاب را بررسی کنید.
نمونه API پیاده سازی شده:
[Route("api/[controller]")] public class TasksController : CrudController<ITaskService, int, TaskReadModel, TaskModel, TaskFilteredPagedQueryModel> { public TasksController(ITaskService service) : base(service) { } protected override string CreatePermissionName => PermissionNames.Tasks_Create; protected override string EditPermissionName => PermissionNames.Tasks_Edit; protected override string ViewPermissionName => PermissionNames.Tasks_View; protected override string DeletePermissionName => PermissionNames.Tasks_Delete; }
Angular 2.0 will be built using the TypeScript language. It will embrace TypeScript's idioms for working with immersive web experiences in larger applications.
You can get those same benefits by working with TypeScript and Angular together. In this session, you'll learn how Angular and TypeScript work together to create single page applications. You'll see how you can leverage the features of ECMAScript 6, and still support today's browsers. You'll see how adopting TypeScript can be as easy as changing the extensions on your .js files. How you use the TypeScript features is completely in your control.
<cdk-virtual-scroll-viewport></cdk-virtual-scroll-viewport>
npm install -g @angular/cli
ng new angular7-virtualScrolling
npm install @angular/cdk@latest
title = 'Angular 7 – Virtual Scrolling feature'; scrollItems: number[] = []; constructor() { for (let index = 0; index < 10000; index++) { this.scrollItems.push(index); } }
<div> <h4> {{this.title}} </h4> <cdk-virtual-scroll-viewport itemSize="100"> <div *cdkVirtualFor="let n of scrollItems">Item {{n}}</div> </cdk-virtual-scroll-viewport> </div>
تمام ! اکنون پروژه را اجرا کنید.
در اولین بار اجرا :
آشنایی با NUnit
NUnit یکی از فریم ورکهای آزمایش واحد سورس باز مخصوص دات نت فریم ورک است. (کلا در دات نت هرجایی دیدید که N ، به ابتدای برنامهای یا کتابخانهای اضافه شده یعنی نمونه منتقل شده از محیط جاوا به دات نت است. برای مثال NHibernate از Hibernate جاوا گرفته شده است و الی آخر)
این برنامه با سی شارپ نوشته شده است اما تمامی زبانهای دات نتی را پشتیبانی میکند (اساسا با زبان نوشته شده کاری ندارد و فایل اسمبلی برنامه را آنالیز میکند. بنابراین فرقی نمیکند که در اینجا چه زبانی بکار گرفته شده است).
ابتدا NUnit را دریافت نمائید:
http://nunit.org/index.php?p=download
یک برنامه ساده از نوع console را در VS.net آغاز کنید.
کلاس MyList را با محتوای زیر به پروژه اضافه کنید:
using System.Collections.Generic;
namespace sample
{
public class MyList
{
public static List<int> GetListOfIntItems(int numberOfItems)
{
List<int> res = new List<int>();
for (int i = 0; i < numberOfItems; i++)
res.Add(i);
return res;
}
}
}
اکنون بر روی نام پروژه در قسمت solution explorer کلیک راست کرده و گزینه add->new project را انتخاب کنید. نوع این پروژه را که متدهای آزمایش واحد ما را تشکیل خواهد داد، class library انتخاب کنید. با نام مثلا TestLibrary (شکل زیر).
با توجه به اینکه NUnit ، اسمبلی برنامه (فایل exe یا dll آنرا) آنالیز میکند، بنابراین میتوان پروژه تست را جدای از پروژه اصلی ایجاد نمود و مورد استفاده قرار داد.
پس از ایجاد پروژه class library ، باید ارجاعی از NUnit framework را به آن اضافه کنیم. به محل نصب NUnit مراجعه کرده (پوشه bin آن) و ارجاعی به فایل nunit.framework.dll را به پروژه اضافه نمائید (شکل زیر).
سپس فضاهای نام مربوطه را به کلاس آزمایش واحد خود اضافه خواهیم کرد:
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
سپس باید ویژگی جدیدی به نام TestFixture را به این کلاس اضافه کرد.
[TestFixture]
public class TestClass
سپس هر متدی که به عنوان متد آزمایش واحد نوشته میشود، باید دارای ویژگی Test باشد تا توسط NUnit بررسی گردد:
[Test]
public void TestGetListOfIntItems()
اکنون برای اینکه بتوانیم متد GetListOfIntItems برنامه خود را در پروژه دیگری تست کنیم، باید ارجاعی را به اسمبلی آن اضافه کنیم. همانند قبل، از منوی project گزینه add reference ، فایل exe برنامه کنسول خود را انتخاب کرده و ارجاعی از آنرا به پروژه class library اضافه میکنیم. بدیهی است امکان اینکه کلاس تست در همان پروژه هم قرار میگرفت وجود داشت و صرفا جهت جداسازی آزمایش از برنامه اصلی اینکار صورت گرفت.
پس از این مقدمات، اکنون متد آزمایش واحد ساده زیر را در نظر بگیرید:
[Test]
public void TestGetListOfIntItems()
{
const int count = 5;
List<int> items = sample.MyList.GetListOfIntItems(count);
Assert.That(items.Count,Is.EqualTo(5));
}
اگر آن را (سطر مربوط به Assert را) کلمه به کلمه بخواهیم به فارسی ترجمه کنیم به صورت زیر خواهد بود:
میخواهیم اثبات کنیم که count مربوط به شیء items مساوی 5 است.
پس از اضافه کردن متد فوق، پروژه را کامپایل نمائید.
اکنون برنامه nunit.exe را اجرا کنید تا NUnit IDE ظاهر شود (در همان دایرکتوری bin مسیر نصب NUnit قرار دارد).
از منوی File آن یک پروژه جدید را آغاز نموده و آنرا ذخیره کنید.
سپس از منوی project آن، با استفاده از گزینه add assembly ، فایل dll کتابخانه تست خود را اضافه نمائید.
احتمالا پس از انجام این عملیات بلافاصله با خطای زیر مواجه خواهید شد:
---------------------------
Assembly Not Loaded
---------------------------
System.ApplicationException : Unable to load TestLibrary because it is not located under
the AppBase
----> System.IO.FileNotFoundException : Could not load file or assembly
'TestLibrary' or one of its dependencies. The system cannot find the file specified.
For further information, use the Exception Details menu item.
همانطور که ملاحظه میکنید، NUnit با استفاده از قابلیتهای reflection در دات نت، اسمبلی را بارگذاری میکند و تمامی کلاسهایی که دارای ویژگی TestFixture باشند در آن لیست خواهد شد.
اکنون بر روی دکمه run کلیک کنید تا اولین آزمایش ما انجام شود. (شکل زیر)
رنگ سبز در اینجا به معنای با موفقیت انجام شدن آزمایش است.
ادامه دارد...
angular-translate دایرکتیو و فیلتر هایی را به صورت کامپوننت عرضه کرده است که شما میتوانید به وسیلهی آنها پروژه خود را به زبانهای گوناگون localize کنید. آنچه که در تصویر فوق مشاهده میکنید در حقیقت ساختار ماژول angular-translate را نمایش میدهد. به این صورت که در اولین لایه دایرکتیو translate قرار گرفته است.
سطح بعدی شاید کمی جذابتر به نظر برسد. هر دو بخش دایرکتیو و فیلتر از سرویس تزریق شده translate$ استفاده میکنند. این بدین معنی است که در هنگام تغییر یک زبان به زبان دیگر به وسیله دایرکتیوهایی که در view نوشته شده است، در حقیقت شما این دایرکتیوها را به سطح فیلتر انتقال دادهاید و پس از آن، فیلتر آن را به لایه سرویس برده و سرویس کار اصلی را انجام میدهد.
در سطح بعدی لایه Interpolator قرار گرفته است که وظیفه تغییر DOMها را بر عهده دارد. این فرآیند متغیرهایی را که در ریسورس قرار دادهایم، درایه به درایه در view و در مکانهای هم نام قرار میدهد. این فرآیند به وسیله راهکارهای گوناگونی از قبیل Message Format و یا Angular Core Interpolation Service امکان پذیر است.
جعبه خاکستری رنگ بخش missing translation handler را نشان میدهد. این handlerها زمانی فراخوانی میگردند که translator به دنبال یک key میگردد که تعریف نشده باشد. angular-translate خود شامل یک logging service میباشد که به صورت extension باید آن را به پروژه خود اضافه نمایید که در بخشهای بعدی در مورد آن مفصلتر بحث میکنیم.
بخش asynchronous loader شما را قادر میسازد که دادههای زبانهای متفاوت را به صورت غیر هم زمان بارگذاری نمایید. angular-translate از ماژولهای asynchronous loader پشتیبانی میکند. روش معرفی شده در سایت مرجع در مورد بارگذاری غیر همزمان urlLoader و staticFilesLoader هستند که در بخشهای بعدی به آن خواهیم پرداخت.
angular-translate برای ذخیره سازی دادههای بازگذاری شده و نگه داشتن زبان سایت در صورت reload شدن صفحه راهکارهای متفاوتی را ارائه نموده است. این قابلیت در ابتدا چک میکند که ریسورسهای زبان درون یک local storage قرار گرفتهاند یا خیر. این عملیات طی یک فرآیند جستجوی key-value صورت میگیرد و در نهایت فایلهای ریسورس زبان مورد نظر که درون کوکی یا localStorage ذخیره شدهاند بارگذاری میشوند. angular-translate به صورت توکار دارای دو راهکار localStorage و cookieStorage برای ذخیره سازی ریسورسها میباشد.
در بخش بعدی در مورد نحوهی ذخیره سازی دادهها به دو روش cookieStorage و localStorage بیشتر صحبت خواهیم کرد.
using System.Threading.Tasks; namespace Async05 { class Program { static void Main(string[] args) { var res = doSomethingAsync().Result; } static async Task<int> doSomethingAsync() { await Task.Delay(1); return 1; } } }
البته باید دقت داشت، زمانیکه از خاصیت Result استفاده میشود، این متد همزمان عمل خواهد کرد و نه غیرهمزمان (ترد جاری را بلاک میکند؛ یکی از موارد مجاز استفاده از آن در متد Main برنامههای کنسول است). همچنین اگر در متد doSomethingAsync استثنایی رخ داده باشد، این استثناء زمان استفاده از Result، به صورت یک AggregateException مجددا صادر خواهد شد. وجود کلمهی Aggregate در اینجا به علت امکان استفادهی تجمعی و ترکیب چندین Task باهم و داشتن چندین شکست و استثنای ممکن است.
همچنین اگر از کلمهی کلیدی await بر روی یک faulted task استفاده کنیم، AggregateException صادر نمیشود. در این حالت کامپایلر AggregateException را بررسی کرده و آنرا تبدیل به یک Exception متداول و معمول کدهای دات نت میکند. به عبارتی سعی شدهاست در این حالت، رفتار کدهای async را شبیه به رفتار کدهای متداول همزمان شبیه سازی کنند.
یک مثال
در اینجا توسط متد getTitleAsync، اطلاعات یک صفحهی وب به صورت async دریافت شده و سپس عنوان آن استخراج میشود. در متد showTitlesAsync نیز از آن استفاده شده و در طی یک حلقه، چندین وب سایت مورد بررسی قرار خواهند گرفت. چون متد getTitleAsync از نوع async تعریف شدهاست، فراخوان آن نیز باید async تعریف شود تا بتوان از واژهی کلیدی await برای کار با آن استفاده کرد.
نهایتا در متد Main برنامه، وظیفهی غیرهمزمان showTitlesAsync اجرا شده و تا پایان عملیات آن صبر میشود. چون خروجی آن از نوع Task است و نه Task of T، در اینجا دیگر خاصیت Result قابل دسترسی نیست. متد Wait نیز ترد جاری را همانند خاصیت Result بلاک میکند.
using System; using System.Collections.Generic; using System.Net; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Async05 { class Program { static void Main(string[] args) { var task = showTitlesAsync(new[] { "http://www.google.com", "https://www.dntips.ir" }); task.Wait(); Console.WriteLine(); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } static async Task showTitlesAsync(IEnumerable<string> urls) { foreach (var url in urls) { var title = await getTitleAsync(url); Console.WriteLine(title); } } static async Task<string> getTitleAsync(string url) { var data = await new WebClient().DownloadStringTaskAsync(url); return getTitle(data); } private static string getTitle(string data) { const string patternTitle = @"(?s)<title>(.+?)</title>"; var regex = new Regex(patternTitle); var mc = regex.Match(data); return mc.Groups.Count == 2 ? mc.Groups[1].Value.Trim() : string.Empty; } } }
برنامه را در حالت عدم اتصال به اینترنت اجرا کنید. استثنای صادر شده، در متد task.Wait ظاهر میشود (چون متدهای async ترد جاری را خالی کردهاند):
و اگر در اینجا بر روی لینک View details کلیک کنیم، در inner exception حاصل، خطای واقعی قابل مشاهده است:
همانطور که ملاحظه میکنید، استثنای صادر شده از نوع System.AggregateException است. به این معنا که میتواند حاوی چندین استثناء باشد که در اینجا تعداد آنها با عدد یک مشخص شدهاست. بنابراین در این حالات، بررسی inner exception را فراموش نکنید.
در ادامه داخل حلقهی foreach متد showTitlesAsync، یک try/catch قرار میدهیم:
static async Task showTitlesAsync(IEnumerable<string> urls) { foreach (var url in urls) { try { var title = await getTitleAsync(url); Console.WriteLine(title); } catch (Exception ex) { Console.WriteLine(ex); } } }
System.Net.WebException: The remote server returned an error: (502) Bad Gateway. System.Net.WebException: The remote server returned an error: (502) Bad Gateway. Press any key to exit...
مدیریت تمام inner exceptionهای رخ داده در پردازشهای موازی
همانطور که عنوان شد، await تنها یک استثنای حاصل از Task در حال اجرا را به کد فراخوان بازگشت میدهد. در این حالت اگر این Task، چندین شکست را گزارش دهد، چطور باید برای دریافت تمام آنها اقدام کرد؟ برای مثال استفاده از Task.WhenAll میتواند شامل چندین استثنای حاصل از چندین Task باشد، ولی await تنها اولین استثنای دریافتی را بازگشت میدهد. اما اگر از خاصیتی مانند Result یا متد Wait استفاده شود، یک AggregateException حاصل تمام استثناءها را دریافت خواهیم کرد. بنابراین هرچند await تنها اولین استثنای دریافتی را بازگشت میدهد، اما میتوان به Taskهای مرتبط مراجعه کرد و سپس بررسی نمود که آیا استثناهای دیگری نیز وجود دارند یا خیر؟
برای نمونه در مثال فوق، حلقهی foreach تشکیل شده آنچنان بهینه نیست. از این جهت که هر بار تنها یک سایت را بررسی میکند، بجای اینکه مانند مرورگرها چندین ترد را به یک یا چند سایت باز کرده و نتایج را دریافت کند.
البته انجام کارها به صورت موازی همیشه ایدهی خوبی نیست ولی حداقل در این حالت خاص که با یک یا چند سرور راه دور کار میکنیم، درخواستهای همزمان دریافت اطلاعات، سبب کارآیی بهتر برنامه و بالا رفتن سرعت اجرای آن میشوند. اما مثلا در حالتیکه با سخت دیسک سیستم کار میکنیم، اجرای موازی کارها نه تنها کمکی نخواهد کرد، بلکه سبب خواهد شد تا مدام drive head در مکانهای مختلفی مشغول به حرکت شده و در نتیجه کارآیی آن کاهش یابد.
برای ترکیب چندین Task، ویژگی خاصی به زبان سیشارپ اضافه نشده، زیرا نیازی نبوده است. برای این حالت تنها کافی است از متد Task.WhenAll، برای ساخت یک Task مرکب استفاده کرد. سپس میتوان واژهی کلیدی await را بر روی این Task مرکب فراخوانی کرد.
همچنین میتوان از متد ContinueWith یک Task مرکب نیز برای جلوگیری از بازگشت صرفا اولین استثنای رخ داده توسط کامپایلر، استفاده کرد. در این حالت امکان دسترسی به خاصیت Result آن به سادگی میسر میشود که حاوی AggregateException کاملی است.
اعتبارسنجی آرگومانهای ارسالی به یک متد async
زمان اعتبارسنجی آرگومانهای ارسالی به متدهای async مهم است. بعضی از مقادیر را نمیتوان بلافاصله اعتبارسنجی کرد؛ مانند مقادیری که نباید نال باشند. تعدادی دیگر نیز پس از انجام یک Task زمانبر مشخص میشوند که معتبر بودهاند یا خیر. همچنین فراخوانهای این متدها انتظار دارند که متدهای async بلافاصله بازگشت داده شده و ترد جاری را خالی کنند. بنابراین اعتبارسنجیهای آنها باید با تاخیر انجام شود. در این حالات، دو نوع استثنای آنی و به تاخیر افتاده را شاهد خواهیم بود. استثنای آنی زمان شروع به کار متد صادر میشود و استثنای به تاخیر افتاده در حین دریافت نتایج از آن دریافت میگردد. باید دقت داشت کلیه استثناهای صادر شده در بدنهی یک متد async، توسط کامپایلر به عنوان یک استثنای به تاخیر افتاده گزارش داده میشود. بنابراین اعتبارسنجیهای آرگومانها را بهتر است در یک متد سطح بالای غیر async انجام داد تا بلافاصله بتوان استثناءهای حاصل را دریافت نمود.
از دست دادن استثناءها
فرض کنید مانند مثال قسمت قبل، دو وظیفهی async آغاز شده و نتیجهی آنها پس از await هر یک، با هم جمع زده میشوند. در این حالت اگر کل عملیات را داخل یک قطعه کد try/catch قرار دهیم، اولین await ایی که یک استثناء را صادر کند، صرفنظر از وضعیت await دوم، سبب اجرای بدنهی catch میشود. همچنین انجام این عملیات بدین شکل بهینه نیست. زیرا ابتدا باید صبر کرد تا اولین Task تمام شود و سپس دومین Task شروع گردد و به این ترتیب پردازش موازی Taskها را از دست خواهیم داد. در یک چنین حالتی بهتر است از متد await Task.WhenAll استفاده شود. در اینجا دو Task مورد نیاز، تبدیل به یک Task مرکب میشوند. این Task مرکب تنها زمانی خاتمه مییابد که هر دوی Task اضافه شده به آن، خاتمه یافته باشند. به این ترتیب علاوه بر اجرای موازی Taskها، امکان دریافت استثناءهای هر کدام را نیز به صورت تجمعی خواهیم داشت.
مشکل! همانطور که پیشتر نیز عنوان شد، استفاده از await در اینجا سبب میشود تا کامپایلر تنها اولین استثنای دریافتی را بازگشت دهد و نه یک AggregateException نهایی را. روش حل آنرا نیز عنوان کردیم. در این حالت بهتر است از متد ContinueWith و سپس استفاده از خاصیت Result آن برای دریافت کلیه استثناءها کمک گرفت.
حالت دوم از دست دادن استثناءها زمانیاست که یک متد async void را ایجاد میکنید. در این حالات بهتر است از یک Task بجای بازگشت void استفاده شود. تنها علت وجودی async voidها، استفاده از آنها در روالهای رویدادگردان UI است (در سایر حالات code smell درنظر گرفته میشود).
public async Task<double> GetSum2Async() { try { var task1 = GetNumberAsync(); var task2 = GetNumberAsync(); var compositeTask = Task.WhenAll(task1, task2); await compositeTask.ContinueWith(x => { }); return compositeTask.Result[0] + compositeTask.Result[1]; } catch (Exception ex) { //todo: log ex throw; } }
در این مثال دیگر مانند مثال قسمت قبل
public async Task<double> GetSumAsync() { var leftOperand = await GetNumberAsync(); var rightOperand = await GetNumberAsync(); return leftOperand + rightOperand; }
با کمک متد Task.WhenAll ترکیب آنها ایجاد و سپس با فراخوانی await، سبب اجرای موازی چندین Task با هم شدهایم.
مدیریت خطاهای مدیریت نشده
ابتدا مثال زیر را در نظر بگیرید:
using System; using System.Threading.Tasks; namespace Async01 { class Program { static void Main(string[] args) { Test2(); Test(); Console.ReadLine(); GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadLine(); } public static async Task Test() { throw new Exception(); } public static async void Test2() { throw new Exception(); } } }
اگر برنامه را کامپایل کنید، کامپایلر بر روی سطر فراخوانی متد Test اخطار زیر را صادر میکند. البته برنامه بدون مشکل کامپایل خواهد شد.
Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
همچنین اگر برنامه را اجرا کنید استثنای صادر شده در متد async void سبب کرش برنامه میشود؛ اما نه استثنای صادر شده در متد async Task. متدهای async void چون دارای Synchronization Context نیستند، استثنای صادره را به Thread pool برنامه صادر میکنند. به همین جهت در همان لحظه نیز سبب کرش برنامه خواهند شد. اما در حالت async Task به این نوع استثناءها اصطلاحا Unobserved Task Exception گفته شده و سبب بروز faulted state در Task تعریف شده میگردند.
برای مدیریت آنها در سطح برنامه باید در ابتدای کار و در متد Main، توسط TaskScheduler.UnobservedTaskException روال رخدادگردانی را برای مدیریت اینگونه استثناءها تدارک دید. زمانیکه GC شروع به آزاد سازی منابع میکند، این استثناءها نیز درنظر گرفته شده و سبب کرش برنامه خواهند شد. با استفاده از متد SetObserved همانند قطعه کد زیر، میتوان از کرش برنامه جلوگیری کرد:
using System; using System.Threading.Tasks; namespace Async01 { class Program { static void Main(string[] args) { TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; //Test2(); Test(); Console.ReadLine(); GC.Collect(); GC.WaitForPendingFinalizers(); Console.ReadLine(); } private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { e.SetObserved(); Console.WriteLine(e.Exception); } public static async Task Test() { throw new Exception(); } public static async void Test2() { throw new Exception(); } } }
به عبارتی رفتار قطعه کد زیر در دات نت 4 و 4.5 متفاوت است:
Task.Factory.StartNew(() => { throw new Exception(); }); Thread.Sleep(100); GC.Collect(); GC.WaitForPendingFinalizers();
<configuration> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration>
یک نکتهی تکمیلی: ممکن است عبارات lambda مورد استفاده، از نوع async void باشد.
همانطور که عنوان شد باید از async void منهای مواردی که کار مدیریت رویدادهای عناصر UI را انجام میدهند (مانند برنامههای ویندوز 8)، اجتناب کرد. چون پایان کار آنها را نمیتوان تشخیص داد و همچنین کامپایلر نیز اخطاری را در مورد استفاده ناصحیح از آنها بدون await تولید نمیکند (چون نوع void اصطلاحا awaitable نیست). به علاوه بروز استثناء در آنها، بلافاصله سبب خاتمه برنامه میشود. بنابراین اگر جایی در برنامه متد async void وجود دارد، قرار دادن try/catch داخل بدنهی آن ضروری است.
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState) { try { ClickMeButton.Tapped += async (sender, args) => { throw new Exception(); }; } catch (Exception ex) { // This won’t catch exceptions! TextBlock1.Text = ex.Message; } }
public delegate void TappedEventHandler(object sender, TappedRoutedEventArgs e);
class="modified valid form-control"
class="modified invalid form-control"
EditContext = new EditContext(Model); EditContext.SetFieldCssClassProvider(new BootstrapFieldCssClassProvider());
using System; using System.Linq; using Microsoft.AspNetCore.Components.Forms; namespace BlazorComponents { /// <summary> /// Supplies CSS class names for form fields to represent their validation state or other state information from an EditContext. /// </summary> public class BootstrapFieldCssClassProvider : FieldCssClassProvider { /// <summary> /// Gets a string that indicates the status of the specified field as a CSS class. /// </summary> public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier) { if (editContext == null) { throw new ArgumentNullException(nameof(editContext)); } var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any(); if (editContext.IsModified(fieldIdentifier)) { return isValid ? "is-valid" : "is-invalid"; } return isValid ? "" : "is-invalid"; } } }
فعال سازی استفادهی از Tag Helpers برای تمام Viewهای برنامه
برای اینکه تمام Viewهای سایت بتوانند به امکانات Tag Helpers دسترسی پیدا کنند، باید یک سطر ذیل را به فایل ViewImports.cshtml_ اضافه کرد:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Microsoft.AspNetCore.Mvc.TagHelpers به همراه افزودن وابستگی Microsoft.AspNetCore.Mvc در حین فعال سازی ASP.NET MVC، به پروژه اضافه میشود:
فعال سازی Intellisense مربوط به Tag Helpers در ویژوال استودیو
هرچند فعال سازی ASP.NET MVC، تنها وابستگی است که برای کار با Tag Helpers نیاز است، اما برای فعال سازی Intellisense آنها باید بستهی Microsoft.AspNetCore.Razor.Tools را نیز به فایل prject.json برنامه، جهت نصب معرفی کرد:
{ "dependencies": { //same as before "Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0", "Microsoft.AspNetCore.Razor.Runtime": "1.0.0", "Microsoft.AspNetCore.Razor.Tools": { "version": "1.0.0-preview2-final", "type": "build" } }, "tools": { //same as before "Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final" } }
یک مثال: ایجاد لینکی به یک اکشن متد
<a asp-controller="Home" asp-action="Index" asp-route-id="123">Home</a>
اگر نیاز به اشارهی به مسیریابی خاصی از طریق نام آن وجود دارد (همان نامهایی که در حین تعریف یک مسیریابی ذکر میشوند) میتوان به صورت ذیل عمل کرد:
<a asp-route="login">Login</a>
<a asp-controller="Account" asp-action="Register" asp-protocol="https" asp-host="asepecificdomain.com" asp-fragment="fragment">Register</a>
راهنمای تبدیل HTML Helpers به Tag Helpers
در جدول ذیل، مثالهایی را از HTML Helpers متداول و معادلهای Tag Helper آنها مشاهده میکنید:
Tag Helper | HTML Helper |
<label asp-for="Email" class="col-md-2 control-label"></label> | @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" }) |
<a asp-controller="MyController" asp-action="MyAction" class="my-css-classname" my-attr="my-attribute">Click me</a> | @Html.ActionLink("Click me", "MyController", "MyAction", { @class="my-css-classname", data_my_attr="my-attribute"}) |
<input asp-for="FirstName" style="width:100px;"/> | @Html.TextBox("FirstName", Model.FirstName, new { style = "width: 100px;" }) |
<input asp-for="Email" class="form-control" /> | @Html.TextBoxFor(m => m.Email, new { @class = "form-control" }) |
<input asp-for="Password" class="form-control" /> | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) |
<input asp-for="UserName" class="form-control" /> | @Html.EditorFor(l => l.UserName, new { htmlAttributes = new { @class = "form-control" } }) |
<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form"> | @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) { @Html.AntiForgeryToken() |
<span asp-validation-for="UserName" class="text-danger"></span> | @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" }) |
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div> | @Html.ValidationSummary("", new { @class = "text-danger" }) |
نکات تکمیلی کار با فرمها توسط Tag Helpers
نمونهای از مثال Tag helper کار با فرمها را در جدول فوق ملاحظه میکنید. چند نکتهی تکمیلی ذیل را میتوان به آن اضافه کرد:
- در حین کار با Tag Helpers، درج anti forgery token به صورت خودکار صورت میگیرد. اگر میخواهید که این توکن ذکر نشود، آنرا توسط ویژگی "asp-anti-forgery="false خاموش کنید.
- برای درج پارامترهای مسیریابی خاص، از asp-route به همراه نام پارامتر مدنظر استفاده کنید:
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" > </form>
<form action="/Account/Login?returnurl=%2FHome%2FAbout" method="post">
<form asp-route="login" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" > </form>
Tag helpers مخصوص تعریف اسکریپتها و CSSها
در اینجا Tag Helpers صرفا به عنوان جایگزینهای HTML Helpers مطرح نیستند. توسط آنها قابلیتهای جدیدی نیز ارائه شدهاست. برای مثال اگر تگ اسکریپت را به صورت ذیل تعریف کنیم:
<script asp-src-include="~/app/**/*.js"></script>
<script src="/app/app.js"></script> <script src="/app/controllers/controller1.js"></script> <script src="/app/controllers/controller2.js"></script> <script src="/app/controllers/controller3.js"></script> <script src="/app/controllers/controller4.js"></script> <script src="/app/services/service1.js"></script> <script src="/app/services/service2.js"></script>
در این بین اگر میخواهید از پوشهی خاصی صرفنظر کنید، از asp-src-exclude استفاده کنید:
<script asp-src-include="~/app/**/*.js" asp-src-exclude="~/app/services/**/*.js"> </script>
<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css" asp-fallback-test-class="hidden" asp-fallback-test-property="visibility" asp-fallback-test-value="hidden" /> <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/bootstrap.min.js" asp-fallback-src="~/lib/bootstrap/js/bootstrap.min.js" asp-fallback-test="window.jQuery"> </script>
به علاوه اگر ویژگی asp-file-version را نیز ذکر کنید:
<link rel="stylesheet" href="~/css/site.min.css" asp-file-version="true"/>
<link rel="stylesheet" href="/css/site.min.css?v=UdxKHVNJA5vb1EsG9O9uURFDfEE3j1E3DgwL6NiDGMc" />
یک نکته: ویژگی asp-file-version را برای تصاویر هم میتوان بکار برد:
<img src="~/images/logo.png" alt="company logo" asp-file-version="true" />
<img src="/images/logo.png?v=W2F5D366_nQ2fQqUk3URdgWy2ZekXjHzHJaY5yaiOOk" alt="company logo"/>
بررسی Environment Tag Helper
با متغیرهای محیطی و نحوهی تعریف آنها در قسمتهای قبل آشنا شدیم. در اینجا tag helper سفارشی خاصی برای کار با آنها ارائه شدهاست که شیبه به if/else عمل میکنند:
<environment names="Development"> <link rel="stylesheet" href="~/css/site1.css" /> <link rel="stylesheet" href="~/css/site2.css" /> </environment> <environment names="Staging,Production"> <link rel="stylesheet" href="~/css/site.min.css" asp-file-version="true"/> </environment>
کار با دراپ داونها توسط Tag helpers
فرض کنید ViewModel یک view جهت نمایش یک دراپ داون به این صورت تنظیم شدهاست:
public class CustomerViewModel { public string Vehicle { get; set; } public List<SelectListItem> Vehicles { get; set; }
<select asp-for="Vehicle" asp-items="Model.Vehicles"> </select>