پاسخ به بازخوردهای پروژهها
با توجه به ویژگیها و تغییرات جدید که قراره اضافه بشه، فکر میکنم تا آخر این هفته (دهم آبان ماه) منتشر بشه.
بنابراین بهتر هست که تا قبل زمان انتشار، مستندات سایر بانکها در خصوص پرداخت قبوض اینجا گذاشته بشه تا افزوده بشن.
تشکر از شما.
فعال سازی تنظیمات مسیریابی
یکی دیگر از تغییرات عمدهی ASP.NET Core با نگارشهای قبلی آن، نحوهی مدیریت مسیریابیهای سیستم است. در نگارشهای قبلی مبتنی بر HTTP Moduleها، مسیریابیها توسط یک HTTP Module مخصوص، با pipeline اصلی ASP.NET یکپارچه شدهاند و زمانیکه مسیر درخواستی با تنظیمات سیستم تطابق داشته باشد، پردازش کار به HTTP Handler مخصوص ASP.NET MVC منتقل میشود:
اما در ASP.NET Core مبتنی بر میان افزارها، زیر ساخت مسیریابی به صورت زیر تغییر کردهاست:
میان افزار ASP.NET MVC را که در قسمت قبل فعال کردیم، باید بتواند کنترلر و اکشن متد متناظر با URL درخواستی را مشخص کند. این تصمیم گیری نیز بر اساس تنظیماتی به نام Routing انجام میشود. در قسمت قبل، حالت ساده و پیش فرض این تنظیمات را مورد استفاده قرار دادیم
که مطابق سورس ASP.NET Core، معادل است با فراخوانی متد app.UseMvc، با قالب پیش فرضی به صورت زیر:
قالب مشخص شدهی در اینجا به ASP.NET MVC میگوید که از کدام قسمتهای URL باید نام کلاس کنترلر (کلاس ویژهای که به کلمهی Controller ختم میشود) و نام اکشن متد متناظر با آنرا انتخاب کند (اکشن متد، متدی است عمومی در آن کلاس).
روش دیگر معرفی این تنظیمات، استفاده از Attribute routing است:
مسیریابیهای قراردادی
در قسمت قبل، یک POCO Controller را به صورت ذیل تعریف کردیم و این کنترلر، بدون تعریف هیچ نوع مسیریابی خاصی در دسترس بود:
علت کار کردن مسیریابی آن نیز به ذکر متد app.UseMvcWithDefaultRoute در کلاس آغازین برنامه بر میگردد و همانطور که عنوان شد، این فراخوانی را میتوان با فراخوانی واضحتر ذیل جایگزین کرد:
پارامتر این متد که جایگزین متد ConfigureRoutes، در نگارشهای قبلی ASP.NET MVC شدهاست، از نوع IRouteBuilder میباشد.
در این تعاریف، هر کدام از قسمتهای قرارگرفتهی داخل {}، مشخص کنندهی قسمتی از URL دریافتی بوده و نامهای controller و action در اینجا جزو نامهای از پیش مشخص شده هستند و برای نگاشت اطلاعات مورد استفاده قرار میگیرند. برای مثال اگر آدرس home/index/ درخواست شد، برنامه به کلاس HomeController و متد عمومی Index آن هدایت میشود. همچنین قسمت آخر این پردازش به ?id ختم شدهاست. وجود ?، به معنای اختیاری بودن این پارامتر است و اگر در URL ذکر شود، به پارامتر id این اکشن متد، نگاشت خواهد شد. مواردی که پس از = ذکر شدهاند، مقادیر پیش فرض مسیریابی هستند. برای مثال اگر صرفا آدرس home/ درخواست شود، مقدار اکشن متد آن با مقدار پیش فرض index جایگزین خواهد شد و اگر تنها مسیر / درخواست شود، کنترل Home و اکشن متد Index آن پردازش میشوند.
در اینجا به هر تعدادی که نیاز است میتوان متدهای routes.MapRoute را فراخوانی و استفاده کرد؛ اما ترتیب تعریف آنها حائز اهمیت است. هر مسیریابی که در ابتدای لیست اضافه شود، حق تقدم بالاتری خواهد داشت و هر تطابقی با یکی از مسیریابیهای تعریف شده، در همان سطح سبب خاتمهی پردازش سایر مسیریابیها میشود.
استفاده از Attributes برای تعریف مسیریابیها
بجای تعریف قرار دادهای پیش فرض مسیریابی در کلاس آغازین برنامه، میتوان از ویژگی Route نیز استفاده کرد. هرچند روش تعریف مسیریابیهای قراردادی، از نگارشهای آغازین ASP.NET MVC به همراه آن بودهاند، اما با زیاد شدن تعداد کنترلرها و مسیریابیهای سفارشی هر کدام، اینبار با نگاه کردن به یک کنترلر، سریع نمیتوان تشخیص داد که چه مسیریابیهای خاصی به آن مرتبط هستند. برای ساده سازی مدیریت برنامههای بزرگ و ساده سازی تعاریف مسیریابیهای خاص آنها، استفاده از ویژگی Route نیز به ASP.NET MVC اضافه شدهاست.
یک مثال: کنترلر About را درنظر بگیرید:
این کلاس و کنترلر، به صورت پیش فرض نیاز به تعریف هیچ نوع مسیریابی جدیدی ندارد. همان مسیریابی پیش فرض ثبت شدهی در کلاس آغازین برنامه، تمام متدهای عمومی آن یا همان اکشن متدهای آنرا پوشش میدهد. برای مثال جهت رسیدن به اکشن متد SiteName آن، میتوان آدرس /About/SiteName/ را درخواست داد.
اما اگر آدرس /About/ را درخواست دهیم چطور؟ چون در مسیریابی پیش فرض، تعریف {action=Index} را داریم، یعنی هر زمانیکه در URL درخواستی، قسمت action آن ذکر نشد، آنرا با index جایگزین کن و این کنترلر دارای متد Index نیست. در ادامه اگر بخواهیم متد Hello را تبدیل به متد پیش فرض این کنترلر کنیم، میتوان با استفاده از ویژگی Route به صورت ذیل عمل کرد:
در اینجا با اولین Route تعریف شده، مشخص کردهایم که اگر قسمت اول URL درخواستی معادل about بود، پردازش برنامه باید به این کنترلر هدایت شود. بدیهی است الزامی به یکی بودن نام Route، با نام کنترلر، وجود ندارد. همچنین Route تعریف شدهی با رشتهی خالی، به معنای مسیریابی پیش فرض است. یعنی اگر آدرس /about/ درخواست داده شد، اکشن متد پیش فرض آن، متد Hello خواهد بود. در این حالت، ذکر Route بعدی برای اکشن متد SiteName الزامی است و اگر اینکار صورت نگیرد، به استثنای ذیل خواهیم رسید:
که عنوان کردهاست در این حالت مشخص نیست که اکشن متد پیش فرض، کدام است.
روش بهتر و refactoring friendly آن نیز به صورت ذیل است:
عموما مرسوم است که نام مسیریابی کنترلر همان نام کنترلر باشد و نام مسیریابی اکشن متد، همان نام اکشن متد مربوطه. به همین جهت میتوان از توکنهای ویژهی [controller] و [action] نیز در اینجا استفاده کرد که دقیقا به همان نام کنترلر و اکشن متد متناظر با آنها تفسیر خواهند شد. مزیت اینکار این است که در صورت تغییر نام متدها یا کنترلرها، دیگر نیازی نیست تا نامهای تعریف شدهی در ویژگیهای Route را نیز تغییر داد.
یک نکته: در حین تعریف مسیریابی یک کنترلر میتوان پیشوندهایی را نیز ذکر کرد؛ برای مثال:
وجود api در اینجا به این معنا است که از این پس تنها آدرس /api/about/ پردازش خواهد شد و اگر صرفا آدرس /about/ درخواست شود، با خطای 404 و یا یافت نشد، کار خاتمه مییابد.
تعریف قیود، برای مسیریابیهای تعریف شده
فرض کنید به کنترلر About فوق، اکشن متد ذیل را که یک خروجی JSON را بازگشت میدهد، اضافه کردهایم:
در اینجا تعریف مسیریابی آن با users/ و user معانی کاملا متفاوتی را دارند. اگر مسیریابی Users/{userid}/ را تعریف کنیم، یعنی مسیر ذیل از ریشهی سایت باید درخواست شود: http://localhost:7742/users/1
و اگر مسیریابی Users/{userid} را تعریف کنیم، یعنی این مسیریابی پس از ذکر کنترلر about، به عنوان یک اکشن متد آن مفهوم پیدا میکند:
http://localhost:7742/about/users/1
در هر دو حالت، ذکر پارامتر userid الزامی است (چون با ? مشخص نشدهاست)؛ مانند:
در اینجا اگر بخواهیم نوع پارامتر درخواستی را نیز دقیقا مشخص و مقید کنیم، میتوان به صورت ذیل عمل کرد:
اگر این کار را انجام ندهیم، با درخواست مسیر http://localhost:7742/dnt/about/users/test مقدار صفر به userId ارسال میشود (چون پارامتر test عددی نیست). اگر تنظیم فوق را انجام دهیم، کاربر خطای 404 را دریافت میکند.
قیودی را که در اینجا میتوان ذکر کرد به شرح زیر هستند:
• alpha - معادل است با (a-z, A-Z).
• bool - برای تطابق با مقادیر بولی.
• datetime - برای تطابق با تاریخ میلادی.
• decimal - برای تطابق با ورودیهای اعشاری.
• double - برای تطابق با اعداد اعشاری 64 بیتی.
• float - برای تطابق با اعداد اعشاری 32 بیتی.
• guid - برای تطابق با GUID ها
• int - برای تطابق با اعداد صحیح 32 بیتی.
• length - برای تعیین طول رشته.
• long - برای تطابق با اعداد صحیح 64 بیتی.
• max - برای ذکر حداکثر مقدار یک عدد صحیح.
• maxlength - جهت ذکر حداکثر طول رشتهی مجاز ورودی.
• min - برای ذکر حداقل مقدار یک عدد صحیح.
• minlength - جهت ذکر حداقل طول رشتهی مجاز ورودی.
• range - ذکر بازهی اعداد صحیح مجاز.
• regex - ذکر یک عبارت با قاعده جهت مشخص سازی الگوی قابل پذیرش.
برای ترکیب چندین قید مختلف نیز میتوان از : استفاده کرد:
ذکر نام Route برای ساده سازی تعریف آدرسی به آن
در حین تعریف یک Route میتوان نام دلخواهی را نیز به آن انتساب داد (همانند نام default مسیریابی ثبت شدهی در کلاس آغازین برنامه):
مزیت آن این است که اکنون برای اشارهی به این مسیریابی خاص میتوان از این نام تعریف شده استفاده کرد:
پارامتر اول ذکر شده، نام مسیریابی و پارامتر دوم، پارامترهای مرتبط با این مسیریابی هستند.
مشخص سازی ترتیب پردازش مسیریابیها
ترتیب مسیریابیهای ثبت شدهی در کلاس آغازین برنامه، همان ترتیب افزوده شدن و ذکر آنها است.
در اینجا میتوان از خاصیت order نیز استفاده کرد و اعداد کوچکتر، ابتدا پردازش میشوند (مقدار پیش فرض آن نیز صفر است):
امکان تعریف قیود سفارشی
اگر قیودی که تا اینجا ذکر شدند، برای کار شما مناسب نبودند و نیاز بود تا الگوریتم خاصی را جهت محدود سازی دسترسی به یک مسیریابی خاص پیاده سازی کنید، میتوان به صورت ذیل عمل کرد:
در اینجا یک کلاس جدید را که اینترفیس IRouteConstraint را پیاده سازی میکند تعریف کردهایم:
سپس در متد match آن بررسی کردهایم که اگر userid=10 بود، خطای 404 صادر شود.
در آخر برای ثبت و معرفی آن باید به متد ConfigureServices کلاس آغازین برنامه مراجعه کرد:
پس از آن، این نام جدید ثبت شدهی در اینجا، به نحو ذیل قابل استفاده است:
به این ترتیب userid باید از نوع int بوده و همچنین قید custom را نیز پوشش دهد (یعنی userid=10 نباشد).
یک نکته: اگر به سورس ASP.NET Core مراجعه کنید ، تمام قیودی را که پیشتر نام بردیم (مانند int، guid و امثال آن) نیز به همین روش تعریف و پیشتر ثبت شدهاند.
معرفی بستهی نیوگت Microsoft.AspNetCore.SpaServices
مسیریابیهای پیش فرض ASP.NET Core با مسیریابیهای برنامههای SPA مانند AngularJS (و امثال آن) تداخل دارند؛ از این جهت که درخواستهای رسیدهی به سرور، ابتدا به موتور پردازشی ASP.NET وارد میشوند و اگر یافت نشدند، کاربر با پیام 404 مواجه خواهد شد و دیگر در اینجا برنامه به مسیریابی خاص مثلا AngularJS 2.0 هدایت نمیشود.
برای این موارد مرسوم است که یک fallback route را در انتهای مسیریابیهای موجود اضافه کنند (به آن catch all هم میگویند)
در اینجا هر درخواستی که با مسیریابی default تطابق نداشت، توسط الگوی عمومی {url*} پردازش میشود و این پردازش در نهایت سبب راه اندازی برنامهی SPA میگردد. اما مشکل اینجا است که برای فایلهای استاتیک غیرموجود مانند تصاویر، فایلهای js و css نیز خروجی HTML ایی خواهیم داشت؛ بجای خروجی 404 و یافت نشد.
برای حل این مشکل مایکروسافت بستهای را به نام Microsoft.AspNetCore.SpaServices ارائه داده است.
برای افزودن آن بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن Microsoft.AspNetCore.SpaServices را جستجو کرده و نصب نمائید:
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
پس از بازیابی و نصب آن، اکنون catch all را حذف کرده و با یک سطر routes.MapSpaFallbackRoute ذیل جایگزین کنید:
و برای یادآوری مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایلهای استاتیک» در AngularJS 2.0، علاوه بر عمومی کردن پوشهی wwwroot توسط UseFileServer نیاز است پوشهی node_modules را هم با تنظیمات ذیل عمومی کرد و در معرض دید عموم قرار داد (جایی که بستههای node.js نصب میشوند):
یکی دیگر از تغییرات عمدهی ASP.NET Core با نگارشهای قبلی آن، نحوهی مدیریت مسیریابیهای سیستم است. در نگارشهای قبلی مبتنی بر HTTP Moduleها، مسیریابیها توسط یک HTTP Module مخصوص، با pipeline اصلی ASP.NET یکپارچه شدهاند و زمانیکه مسیر درخواستی با تنظیمات سیستم تطابق داشته باشد، پردازش کار به HTTP Handler مخصوص ASP.NET MVC منتقل میشود:
اما در ASP.NET Core مبتنی بر میان افزارها، زیر ساخت مسیریابی به صورت زیر تغییر کردهاست:
میان افزار ASP.NET MVC را که در قسمت قبل فعال کردیم، باید بتواند کنترلر و اکشن متد متناظر با URL درخواستی را مشخص کند. این تصمیم گیری نیز بر اساس تنظیماتی به نام Routing انجام میشود. در قسمت قبل، حالت ساده و پیش فرض این تنظیمات را مورد استفاده قرار دادیم
app.UseMvcWithDefaultRoute();
public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app) { if (app == null) throw new ArgumentNullException("app"); return app.UseMvc((Action<IRouteBuilder>) (routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"))); }
روش دیگر معرفی این تنظیمات، استفاده از Attribute routing است:
[Route("[controller]/[action]")]
مسیریابیهای قراردادی
در قسمت قبل، یک POCO Controller را به صورت ذیل تعریف کردیم و این کنترلر، بدون تعریف هیچ نوع مسیریابی خاصی در دسترس بود:
namespace Core1RtmEmptyTest.Controllers { public class HomeController { public string Index() { return "Running a POCO controller!"; } } }
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
در این تعاریف، هر کدام از قسمتهای قرارگرفتهی داخل {}، مشخص کنندهی قسمتی از URL دریافتی بوده و نامهای controller و action در اینجا جزو نامهای از پیش مشخص شده هستند و برای نگاشت اطلاعات مورد استفاده قرار میگیرند. برای مثال اگر آدرس home/index/ درخواست شد، برنامه به کلاس HomeController و متد عمومی Index آن هدایت میشود. همچنین قسمت آخر این پردازش به ?id ختم شدهاست. وجود ?، به معنای اختیاری بودن این پارامتر است و اگر در URL ذکر شود، به پارامتر id این اکشن متد، نگاشت خواهد شد. مواردی که پس از = ذکر شدهاند، مقادیر پیش فرض مسیریابی هستند. برای مثال اگر صرفا آدرس home/ درخواست شود، مقدار اکشن متد آن با مقدار پیش فرض index جایگزین خواهد شد و اگر تنها مسیر / درخواست شود، کنترل Home و اکشن متد Index آن پردازش میشوند.
در اینجا به هر تعدادی که نیاز است میتوان متدهای routes.MapRoute را فراخوانی و استفاده کرد؛ اما ترتیب تعریف آنها حائز اهمیت است. هر مسیریابی که در ابتدای لیست اضافه شود، حق تقدم بالاتری خواهد داشت و هر تطابقی با یکی از مسیریابیهای تعریف شده، در همان سطح سبب خاتمهی پردازش سایر مسیریابیها میشود.
استفاده از Attributes برای تعریف مسیریابیها
بجای تعریف قرار دادهای پیش فرض مسیریابی در کلاس آغازین برنامه، میتوان از ویژگی Route نیز استفاده کرد. هرچند روش تعریف مسیریابیهای قراردادی، از نگارشهای آغازین ASP.NET MVC به همراه آن بودهاند، اما با زیاد شدن تعداد کنترلرها و مسیریابیهای سفارشی هر کدام، اینبار با نگاه کردن به یک کنترلر، سریع نمیتوان تشخیص داد که چه مسیریابیهای خاصی به آن مرتبط هستند. برای ساده سازی مدیریت برنامههای بزرگ و ساده سازی تعاریف مسیریابیهای خاص آنها، استفاده از ویژگی Route نیز به ASP.NET MVC اضافه شدهاست.
یک مثال: کنترلر About را درنظر بگیرید:
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { public class AboutController : Controller { public ActionResult Hello() { return Content("Hello from DNT!"); } public ActionResult SiteName() { return Content("DNT"); } } }
اما اگر آدرس /About/ را درخواست دهیم چطور؟ چون در مسیریابی پیش فرض، تعریف {action=Index} را داریم، یعنی هر زمانیکه در URL درخواستی، قسمت action آن ذکر نشد، آنرا با index جایگزین کن و این کنترلر دارای متد Index نیست. در ادامه اگر بخواهیم متد Hello را تبدیل به متد پیش فرض این کنترلر کنیم، میتوان با استفاده از ویژگی Route به صورت ذیل عمل کرد:
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { [Route("About")] public class AboutController : Controller { [Route("")] public ActionResult Hello() { return Content("Hello from DNT!"); } [Route("SiteName")] public ActionResult SiteName() { return Content("DNT"); } } }
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied: Core1RtmEmptyTest.Controllers.AboutController.Hello (Core1RtmEmptyTest) Core1RtmEmptyTest.Controllers.AboutController.SiteName (Core1RtmEmptyTest)
روش بهتر و refactoring friendly آن نیز به صورت ذیل است:
using Microsoft.AspNetCore.Mvc; namespace Core1RtmEmptyTest.Controllers { [Route("[controller]")] public class AboutController : Controller { [Route("")] public ActionResult Hello() { return Content("Hello from DNT!"); } [Route("[action]")] public ActionResult SiteName() { return Content("DNT"); } } }
یک نکته: در حین تعریف مسیریابی یک کنترلر میتوان پیشوندهایی را نیز ذکر کرد؛ برای مثال:
[Route("api/[controller]")]
تعریف قیود، برای مسیریابیهای تعریف شده
فرض کنید به کنترلر About فوق، اکشن متد ذیل را که یک خروجی JSON را بازگشت میدهد، اضافه کردهایم:
//[Route("/Users/{userid}")] [Route("Users/{userid}")] public IActionResult GetUsers(int userId) { return Json(new { userId = userId }); }
و اگر مسیریابی Users/{userid} را تعریف کنیم، یعنی این مسیریابی پس از ذکر کنترلر about، به عنوان یک اکشن متد آن مفهوم پیدا میکند:
http://localhost:7742/about/users/1
در هر دو حالت، ذکر پارامتر userid الزامی است (چون با ? مشخص نشدهاست)؛ مانند:
[Route("/Users/{userid:int?}")]
[Route("Users/{userid:int}")]
قیودی را که در اینجا میتوان ذکر کرد به شرح زیر هستند:
• alpha - معادل است با (a-z, A-Z).
• bool - برای تطابق با مقادیر بولی.
• datetime - برای تطابق با تاریخ میلادی.
• decimal - برای تطابق با ورودیهای اعشاری.
• double - برای تطابق با اعداد اعشاری 64 بیتی.
• float - برای تطابق با اعداد اعشاری 32 بیتی.
• guid - برای تطابق با GUID ها
• int - برای تطابق با اعداد صحیح 32 بیتی.
• length - برای تعیین طول رشته.
• long - برای تطابق با اعداد صحیح 64 بیتی.
• max - برای ذکر حداکثر مقدار یک عدد صحیح.
• maxlength - جهت ذکر حداکثر طول رشتهی مجاز ورودی.
• min - برای ذکر حداقل مقدار یک عدد صحیح.
• minlength - جهت ذکر حداقل طول رشتهی مجاز ورودی.
• range - ذکر بازهی اعداد صحیح مجاز.
• regex - ذکر یک عبارت با قاعده جهت مشخص سازی الگوی قابل پذیرش.
برای ترکیب چندین قید مختلف نیز میتوان از : استفاده کرد:
[Route("/Users/{userid:int:max(1000):min(10)}")]
ذکر نام Route برای ساده سازی تعریف آدرسی به آن
در حین تعریف یک Route میتوان نام دلخواهی را نیز به آن انتساب داد (همانند نام default مسیریابی ثبت شدهی در کلاس آغازین برنامه):
[Route("/Users/{userid:int}", Name="GetUserById")]
string uri = Url.Link("GetUserById", new { userid = 1 });
مشخص سازی ترتیب پردازش مسیریابیها
ترتیب مسیریابیهای ثبت شدهی در کلاس آغازین برنامه، همان ترتیب افزوده شدن و ذکر آنها است.
در اینجا میتوان از خاصیت order نیز استفاده کرد و اعداد کوچکتر، ابتدا پردازش میشوند (مقدار پیش فرض آن نیز صفر است):
[Route("/Users/{userid:int}", Name = "GetUserById", Order = 1)]
امکان تعریف قیود سفارشی
اگر قیودی که تا اینجا ذکر شدند، برای کار شما مناسب نبودند و نیاز بود تا الگوریتم خاصی را جهت محدود سازی دسترسی به یک مسیریابی خاص پیاده سازی کنید، میتوان به صورت ذیل عمل کرد:
using System; using System.Globalization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; namespace Core1RtmEmptyTest { public class CustomRouteConstraint : IRouteConstraint { public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection) { object value; if (!values.TryGetValue(routeKey, out value) || value == null) { return false; } long longValue; if (value is long) { longValue = (long)value; return longValue != 10; } var valueString = Convert.ToString(value, CultureInfo.InvariantCulture); if (long.TryParse(valueString, NumberStyles.Integer, CultureInfo.InvariantCulture, out longValue)) { return longValue != 10; } return false; } } }
public class CustomRouteConstraint : IRouteConstraint
در آخر برای ثبت و معرفی آن باید به متد ConfigureServices کلاس آغازین برنامه مراجعه کرد:
public void ConfigureServices(IServiceCollection services) { services.AddRouting(options =>options.ConstraintMap.Add("Custom", typeof(CustomRouteConstraint)));
[Route("/Users/{userid:int:custom}")]
یک نکته: اگر به سورس ASP.NET Core مراجعه کنید ، تمام قیودی را که پیشتر نام بردیم (مانند int، guid و امثال آن) نیز به همین روش تعریف و پیشتر ثبت شدهاند.
معرفی بستهی نیوگت Microsoft.AspNetCore.SpaServices
مسیریابیهای پیش فرض ASP.NET Core با مسیریابیهای برنامههای SPA مانند AngularJS (و امثال آن) تداخل دارند؛ از این جهت که درخواستهای رسیدهی به سرور، ابتدا به موتور پردازشی ASP.NET وارد میشوند و اگر یافت نشدند، کاربر با پیام 404 مواجه خواهد شد و دیگر در اینجا برنامه به مسیریابی خاص مثلا AngularJS 2.0 هدایت نمیشود.
برای این موارد مرسوم است که یک fallback route را در انتهای مسیریابیهای موجود اضافه کنند (به آن catch all هم میگویند)
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapRoute( name: "spa-fallback", template: "{*url}", defaults: new { controller = "Home", action = "Index" }); });
برای حل این مشکل مایکروسافت بستهای را به نام Microsoft.AspNetCore.SpaServices ارائه داده است.
برای افزودن آن بر روی گره references کلیک راست کرده و گزینهی manage nuget packages را انتخاب کنید. سپس در برگهی browse آن Microsoft.AspNetCore.SpaServices را جستجو کرده و نصب نمائید:
انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{ "dependencies": { //same as before "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000007" },
app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" }); });
// Serve wwwroot as root app.UseFileServer(); // Serve /node_modules as a separate root (for packages that use other npm modules client side) app.UseFileServer(new FileServerOptions { // Set root of file server FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")), // Only react to requests that match this path RequestPath = "/node_modules", // Don't expose file system EnableDirectoryBrowsing = false });
مطالب دورهها
شی گرایی در #F
برنامه نویسی شی گرای سومین نسل از الگوهای اصلی برنامه نویسی است. در توضیحات فصل اول گفته شد که #F یک زبان تابع گرا است ولی این بدان معنی نیست که #F از مفاهیمی نظیر کلاس و یا interface پشتیبانی نکند. برعکس در #F امکان تعریف کلاس و interface و هم چنین پیاده سازی مفاهیم شی گرایی وجود دارد.
*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری میکنم.
Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ایترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
همان طور که در ساختار بالا میبینید مفاهیم access-modifier و inherit و constructor هم در #F وجود دارد.
انواع access-modifier در #F
کلاس بالا دارای یک سازنده است که دو پارامتر ورودی میگیرد. کلمه end به معنای انتهای کلاس است. برای استفاده کلاس باید به صورت زیر عمل کنید:
توابع و خواص در کلاس ها
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
کلاس بالا دارای سه خاصیت به نامهای Number و Name و Amount است و دو تابع به نامهای Deposit و Withdraw دارد. اما x استفاده شده قبل از هر member به معنی this در #C است. در #F شما برای اشاره به شناسههای یک محدوده خودتون باید یک نام رو برای اشاره گر مربوطه تعیین کنید.
استفاده از کلمه do
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازندههای کلاسهای دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
یک مثال در این زمینه:
در مثال بالا دو عبارت do یکی به صورت static و دیگری به صورت غیر static تعریف شده اند. استفاده از do به صورت غیر static این امکان را به ما میدهد که بتوانیم به تمام شناسهها و توابع تعریف شده در کلاس استفاده کنیم ولی do به صورت static فقط به خواص و توابع از نوع static در کلاس دسترسی دارد.
خروجی مثال بالا:
خواص static:
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
SomeStaticMethod به صورت استاتیک تعریف شده در حالی که x.Prop به صورت غیر استاتیک. دسترسی به متدها یا خواص static باید بدون وهله سازی از کلاس انجام بگیرد در غیر این صورت با خطای کامپایلر روبرو خواهید شد.
روش استفاده درست:
متدهای get , set در خاصیت ها:
همانند #C و سایر زبانهای دات نت امکان تعریف متدهای get و set برای خاصیتهای یک کلاس وجود دارد.
ساختار کلی:
مثالی در این زمینه:
کد متناظر در #C:
یا به صورت:
Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره میکند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
مثال:
استفاده از حرف I برای شروع نام اینترفیس طبق قوانین تعریف شده (اختیاری) برای نام گذاری است.
نکته: در هنگام تعریف توابع و خاصیت در interfaceها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریسها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی میکند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما میتونید از هر کلمه یا حرف دیگری استفاده کنید.
نکته مهم: اگر قصد فراخوانی متد Print را در کلاس بالا دارید نمیتونید به صورت مستقیم متد بالا را فراخوانی کنید. بلکه حتما باید کلاس به
اینترفیس مربوطه cast شود.
روش نادرست:
روش درست:
برای عملیات cast ازاستفاده کنید.
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث میبرد. در نتیجه باید تمام متدهای هر سه اینترفیس را پیاده سازی کند.
فراخوانی این متدها نیز به صورت زیر خواهد بود:
کلاسهای Abstract
#F از کلاسهای abstract هم پشتیبانی میکند. اگر با کلاسهای abstract در #C آشنایی ندارید میتونید مطالب مورد نظر رو در (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاسهای abstract به عنوان کلاسهای پایه در برنامه نویسی شی گرا استفاده میشوند. این کلاسها دارای خواص و متدهای پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث میبرند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاسهای abstract:
در #F برای این که مشخص کنیم که یک کلاس abstract است حتما باید [<AbstractClass>] در بالای کلاس تعریف شود.
کلاس بالا تعریفی از کلاس abstract است که سه خصوصیت abstract دارد (برای
تعیین خصوصیتها و متد هایی که در کلاس پایه پیاده سازی نمیشوند از کلمه
کلیدی abstract در هنگام تعریف آنها استفاده میکنیم). حال دو کلاس ایجاد میکنیم که این کلاس پایه را پیاده سازی کنند.
#1 کلاس اول
#2 کلاس دوم
Structures
structureها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucureها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
یک نکته مهم هنگام کار با structها در #F این است که امکان استفاده از let
و Binding در structها وجود ندارد. به جای آن باید از val استفاده کنید.
تفاوت اصلی بین val و let در این است که هنگام تعریف
شناسه با val امکان مقدار دهی اولیه به شناسه وجود ندارد. در مثال بالا
مقادیر برای x و y و z برابر 0.0 است که توسط کامپایلر انجام میشود. در
ادامه یک struct به همراه سازنده تعریف میکنیم:
توسط سازنده struct بالا مقادیر اولیه x و y دریافت میشود به متغیرهای متناظر انتساب میشود.
در پایان یک مثال مشترک رو در #C و #F پیاده سازی میکنیم:
*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری میکنم.
Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ایترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] = [ class ] [ inherit base-type-name(base-constructor-args) ] [ let-bindings ] [ do-bindings ] member-list ... [ end ] type [access-modifier] type-name1 ... and [access-modifier] type-name2 ... ...
انواع access-modifier در #F
- public : دسترسی برای تمام فراخوانها امکان پذیر است
- internal : دسترسی برای تمام فراخوان هایی که در همین assembly هستند امکان پذیر است
- private : دسترسی فقط برای فراخوانهای موجود در همین ماژول امکان پذیر است
نکته : protected access modifier در #F پشتیبانی نمیشود.
مثالی از تعریف کلاس:
type Account(number : int, name : string) = class let mutable amount = 0m end
let myAccount = new Account(123456, "Masoud")
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
open System type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
let masoud= new Account(12345, "Masoud") let saeed = new Account(67890, "Saeed") let transfer amount (source : Account) (target : Account) = source.Withdraw amount target.Deposit amount let printAccount (x : Account) = printfn "x.Number: %i, x.Name: %s, x.Amount: %M" x.Number x.Name x.Amount let main() = let printAccounts() = [masoud; saeed] |> Seq.iter printAccount printfn "\nInializing account" homer.Deposit 50M marge.Deposit 100M printAccounts() printfn "\nTransferring $30 from Masoud to Saeed" transfer 30M masoud saeed
printAccounts() printfn "\nTransferring $75 from Saeed to Masoud" transfer 75M saeed masoud printAccounts() main()
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازندههای کلاسهای دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
open System open System.Net type Stock(symbol : string) = class let mutable _symbol = String.Empty do //کد مورد نظر در این جا نوشته میشود end
open System type MyType(a:int, b:int) as this = inherit Object() let x = 2*a let y = 2*b do printfn "Initializing object %d %d %d %d %d %d" a b x y (this.Prop1) (this.Prop2) static do printfn "Initializing MyType." member this.Prop1 = 4*x member this.Prop2 = 4*y override this.ToString() = System.String.Format("{0} {1}", this.Prop1, this.Prop2) let obj1 = new MyType(1, 2)
خروجی مثال بالا:
Initializing MyType. Initializing object 1 2 2 4 8 16
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
type SomeClass(prop : int) = class member x.Prop = prop static member SomeStaticMethod = "This is a static method" end
let instance = new SomeClass(5);; instance.SomeStaticMethod;; output: stdin(81,1): error FS0191: property 'SomeStaticMethod' is static.
SomeClass.SomeStaticMethod;; (* invoking static method *)
همانند #C و سایر زبانهای دات نت امکان تعریف متدهای get و set برای خاصیتهای یک کلاس وجود دارد.
ساختار کلی:
member alias.PropertyName with get() = some-value and set(value) = some-assignment
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = num <- value end;;
public int Num { get{return num;} set{num=value;} }
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = if value > 10 || value < 0 then raise (new Exception("Values must be between 0 and 10")) else num <- value end
Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره میکند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
type type-name = interface inherits-decl member-defns end
type IPrintable = abstract member Print : unit -> unit
نکته: در هنگام تعریف توابع و خاصیت در interfaceها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریسها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی میکند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما میتونید از هر کلمه یا حرف دیگری استفاده کنید.
type SomeClass1(x: int, y: float) = interface IPrintable with member this.Print() = printfn "%d %f" x y
روش نادرست:
let instance = new SomeClass1(10,20) instance.Print//فراخوانی این متد باعث ایجاد خطای کامپایلری میشود.
let instance = new SomeClass1(10,20) let instanceCast = instance :> IPrintable// استفاده از (<:) برای عملیات تبدیل کلاس به اینترفیس instanceCast.Print
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث میبرد. در نتیجه باید تمام متدهای هر سه اینترفیس را پیاده سازی کند.
type Interface1 = abstract member Method1 : int -> int type Interface2 = abstract member Method2 : int -> int type Interface3 = inherit Interface1 inherit Interface2 abstract member Method3 : int -> int type MyClass() = interface Interface3 with member this.Method1(n) = 2 * n member this.Method2(n) = n + 100 member this.Method3(n) = n / 10
let instance = new MyClass() let instanceToCast = instance :> Interface3 instanceToCast.Method3 10
#F از کلاسهای abstract هم پشتیبانی میکند. اگر با کلاسهای abstract در #C آشنایی ندارید میتونید مطالب مورد نظر رو در (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاسهای abstract به عنوان کلاسهای پایه در برنامه نویسی شی گرا استفاده میشوند. این کلاسها دارای خواص و متدهای پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث میبرند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاسهای abstract:
[<AbstractClass>] type [ accessibility-modifier ] abstract-class-name = [ inherit base-class-or-interface-name ] [ abstract-member-declarations-and-member-definitions ] abstract member member-name : type-signature
[<AbstractClass>] type Shape(x0 : float, y0 : float) = let mutable x, y = x0, y0 let mutable rotAngle = 0.0 abstract Area : float with get abstract Perimeter : float with get abstract Name : string with get
#1 کلاس اول
type Square(x, y,SideLength) = inherit Shape(x, y)
override this.Area = this.SideLength * this.SideLength override this.Perimeter = this.SideLength * 4. override this.Name = "Square"
type Circle(x, y, radius) = inherit Shape(x, y)
let PI = 3.141592654 member this.Radius = radius override this.Area = PI * this.Radius * this.Radius override this.Perimeter = 2. * PI * this.Radius
structureها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucureها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
- structureها از نوع مقداری هستند و این بدین معنی است مستقیما درون پشته ذخیره میشوند.
- ارجاع به structureها از نوع ارجاع با مقدار است بر خلاف کلاسها که از نوع ارجاع به منبع هستند.(^ )
- structureها دارای خواص ارث بری نیستند.
- عموما از structure برای ذخیره مجموعه ای از دادهها با حجم و اندازه کم استفاده میشود.
ساختار کلی تعریف structure
[ attributes ] type [accessibility-modifier] type-name = struct type-definition-elements end //یا به صورت زیر [ attributes ] [<StructAttribute>] type [accessibility-modifier] type-name = type-definition-elements
type Point3D = struct val x: float val y: float val z: float end
type Point2D = struct val X: float val Y: float new(x: float, y: float) = { X = x; Y = y } end
در پایان یک مثال مشترک رو در #C و #F پیاده سازی میکنیم:
اشتراکها
چک لیست حضور آنلاین امنتر
اشتراکها
آموزش JavaScript در 30 روز
مطالب
ASP.NET MVC #24
مروری بر نمونه سؤالات ASP.NET MVC امتحانات مایکروسافت در چند سال اخیر
در قسمت آخر سری ASP.NET MVC بد نیست مروری داشته باشیم بر نمونه سؤالات امتحانات مایکروسافت؛ امتحانات 70-515 و 70-519 که در آنها تعدادی از سؤالات به ASP.NET MVC اختصاص دارند. در این سؤالات امکان انتخاب بیش از یک گزینه نیز وجود دارد.
1) شما در حال توسعه یک برنامهی ASP.NET MVC هستید. باید درخواست Ajax ایی از صفحهای صادر شده و خروجی زیر را از اکشن متدی دریافت کند:
["Adventure Works","Contoso"]
کدام نوع خروجی اکشن متد زیر را برای اینکار مناسب میدانید؟
a) AjaxHelper
b) XDocument
c) JsonResult
d) DataContractJsonSerializer
2) شما در حال طراحی یک برنامه ASP.NET MVC هستید. محتوای یک View باید بر اساس نیازمندیهای زیر تشکیل شود:
الف) ارائه محتوای رندر شده user controls/partial views به مرورگر
ب) کار انتخاب user controls/partial views مناسب در اکشن متد کنترلر باید انجام شود
استفاده از کدام روش زیر را توصیه میکنید؟
a) Use the Html.RenderPartial extension method
b) Use the Html.RenderAction extension method
c) Use the PartialViewResult class
d) Use the ContentResult class
3) در حین طراحی یک برنامه ASP.NET MVC، نیاز است منطق مدیریت استثناهای رخ داده و همچنین ثبت وقایع مرتبط را در یک مکان یا کلاس مرکزی مدیریت کنید. کدام روش زیر را پیشنهاد میدهید؟
a) استفاده از try/catch در تمام متدها
b) تحریف متد OnException در کنترلرها
c) مزین سازی تمام کنترلرها به ویژگی HandleError سفارشی شده
d) مزین سازی تمام کنترلرها به ویژگی HandleError پیش فرض
4) شما در حال توزیع برنامهی ASP.NET MVC خود جهت اجرا بر روی IIS 6.x هستید. چه ملاحظاتی را باید مدنظر داشته باشید تا برنامه به درستی کار کند؟
a) تنظیم IIS به نحویکه تمام درخواستها را بر اساس wildcard خاصی به aspnet_isapi.dll هدایت کند.
b) تنظیم IIS به نحویکه تمام درخواستها را بر اساس wildcard خاصی به aspnet_wp.exe هدایت کند.
c) تغییر برنامه به نحویکه تمام درخواستها را به یک HttpHandler خاص هدایت کند.
d) تغییر برنامه به نحویکه تمام درخواستها را به یک HttpModule خاص هدایت کند.
5) شما در حال توسعه برنامهی ASP.NET MVC هستید که در پوشه Views/Shared/DisplayTemplates آن، فایلی به نام score.cshtml به عنوان یک templated helper نمایش سفارشی اعداد صحیح تعریف شده است. مدل برنامه هم مطابق تعاریف زیر است:
public class Player
{
public String Name { get; set; }
public int LastScore { get; set; }
public int HighScore { get; set; }
}
در اینجا اگر نیاز باشد تا فایل score.cshtml یاد شده به صورت خودکار به خاصیت LastScore در حین فراخوانی متد HtmlHelper.DisplayForModel اعمال شود، چه روشی را پیشنهاد میدهید؟
a) فایل score.cshtml باید به LastScore.cshtml تغییر نام یابد.
b) فایل یاد شده باید از پوشه Views/Shared/DisplayTemplates به پوشه Views/Player/DisplayTemplates منتقل شود.
c) باید از ویژگی UIHint به همراه مقدار score جهت مزین سازی خاصیت LastScore استفاده کرد.
[UIHint("Score")]
[Display(Name="LastScore", ShortName="Score")]
6) شما در حال طراحی برنامهی ASP.NET MVC هستید که در آن متد Edit کنترلری باید تنها توسط کاربران اعتبارسنجی شده قابل دسترسی باشد. استفاده از کدام دو گزینه زیر را برای این منظور توصیه میکنید؟
a) [Authorize(Users = "")]
b) [Authorize(Roles = "")]
c) [Authorize(Users = "*")]
d) [Authorize(Roles = "*")]
7) قطعه کد HTML زیر را درنظر بگیرید:
<span id="ref">
<a name=Reference>Check out</a>
the FAQ on
<a href="http://www.contoso.com">
Contoso</a>'s web site for more information:
<a href="http://www.contoso.com/faq">FAQ</a>.
</span>
<a href="http://www.contoso.com/home">Home</a>
قصد داریم به کمک jQuery در span ایی با id مساوی ref، متن تمام لینکها را ضخیم کنیم. کدام گزینه زیر را پیشنهاد میدهید؟
a) $("#ref").filter("a[href]").bold();
b) $("ref").filter("a").css("bold");
c) $("a").css({fontWeight:"bold"});
d) $("#ref a[href]").css({fontWeight:"bold"});
تا قبل از ASP.NET 4.5 ، هنگام کار با GridView رسم بر این بوده که به خاصیت DataSource ، یک منبع داده (مانند SqlDataSource و ...) را Bind کرده و متد DataBind را صدا نموده و نتیجه نمایش داده میشد.
اما با استفاده از ویژگیهای جدید اضافه شده(هر چند با تأخیر نسبت به Gridهای پیشرفته دیگر ) کار با این کنترل راحتتر و خواناتر شده است. یکی از این ویژگیها را با هم بررسی میکنیم:
با استفاده از ویژگی SelectMethod میتوان متدی را به GridView معرفی کرد که وظیفه منبع داده را انجام داده و هنگام Bind فراخوانی شده و گرید را پر کند:
مثال:
نکته مهم در این کد ItemType است. با استفاده از این خاصیت به جای اینکه مانند قبل نام فیلدهایی که قرار است در گرید نمایش داده شود را بصورت string معرفی کنیم (مثلا در اینجا ("Eval("Name ، اگر نام فیلد را غلط بنویسیم هنگام کامپایل خطایی صادر نمیشود)، آنرا بصورت Strongly Type نوشته و از اشتباه جلوگیری میکنیم.( + )
کد متد:
و سپس دستور زیر را فراخوانی میکنیم:
اگر بخواهیم در گرید Paging داشته باشیم بصورت زیر عمل میکنیم:
که در اینجا دو خصوصیت AllowPaging و PageSize را مقدار دهی کرده ایم. این خصوصیتها اجازه صفحه بندی را به گرید میدهند.حال برای اینکه متد نیز برای صفحه بندی آماده شود باید سه آرگومان به آن اضافه کنیم:(نام پارامترها باید دقیقا موارد زیر باشد)
1- startRowIndex: نقطه شروع صفحه بندی را مشخص میکند.
2- maximumRows: تعداد سطرهایی که گرید باید نمایش دهد را مشخص میکند.
3- totalRowCount: این پارامتر باید در تابع مقدار دهی شود (مانند مثال) تا مشخص شود نتیجه Query چند رکورد است و در نهایت گرید تعداد صفحات را بر این اساس نمایش میدهد.
و برای اینکه صفحه بندی را در Query هم لحاظ کنیم از دو تا بع Skip و Take استفاده شده است.
نکته مهم در این متد IQueryable بودن آن است که باعث واکشی دادهها بصورت صفحه به صفحه میشود.
دستورات SQL تولید شده در پروفایلر:
همانطور که مشاهده میکنید دو دستور SQL تولید شده ، یکی برای بازگرداندن تعداد رکوردها و یکی هم برای واکشی دادهها به اندازه تعداد رکوردهای مجاز در هر صفحه.
اما با استفاده از ویژگیهای جدید اضافه شده(هر چند با تأخیر نسبت به Gridهای پیشرفته دیگر ) کار با این کنترل راحتتر و خواناتر شده است. یکی از این ویژگیها را با هم بررسی میکنیم:
با استفاده از ویژگی SelectMethod میتوان متدی را به GridView معرفی کرد که وظیفه منبع داده را انجام داده و هنگام Bind فراخوانی شده و گرید را پر کند:
مثال:
<asp:GridView ID="gvCities" runat="server" AutoGenerateColumns="False" ItemType="WebApplication3.City" SelectMethod="GetAllCities"> <Columns> <asp:TemplateField HeaderText="نام"> <ItemTemplate><%#: Item.Name %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
کد متد:
public IQueryable<City> GetAllCities() { var context = new EFContext(); var q = from c in context.City orderby c.Name select c; return q; }
gvCities.DataBind();
<asp:GridView ID="gvCities" runat="server" AutoGenerateColumns="False" AllowPaging="True" PageSize="10" ItemType="WebApplication3.City" SelectMethod="GetAllCities"> <Columns> <asp:TemplateField HeaderText="نام"> <ItemTemplate><%#: Item.Name %></ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView>
1- startRowIndex: نقطه شروع صفحه بندی را مشخص میکند.
2- maximumRows: تعداد سطرهایی که گرید باید نمایش دهد را مشخص میکند.
3- totalRowCount: این پارامتر باید در تابع مقدار دهی شود (مانند مثال) تا مشخص شود نتیجه Query چند رکورد است و در نهایت گرید تعداد صفحات را بر این اساس نمایش میدهد.
و برای اینکه صفحه بندی را در Query هم لحاظ کنیم از دو تا بع Skip و Take استفاده شده است.
public IQueryable<City> GetAllCities(int startRowIndex, int maximumRows, out int totalRowCount) { var context = new EFContext(); var q = from c in context.City select c; totalRowCount = q.Count(); return q.OrderBy(x=>x.Name).Skip(startRowIndex).Take(maximumRows); }
نکته مهم در این متد IQueryable بودن آن است که باعث واکشی دادهها بصورت صفحه به صفحه میشود.
دستورات SQL تولید شده در پروفایلر:
همانطور که مشاهده میکنید دو دستور SQL تولید شده ، یکی برای بازگرداندن تعداد رکوردها و یکی هم برای واکشی دادهها به اندازه تعداد رکوردهای مجاز در هر صفحه.