نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 7 - بررسی رابطه‌ی One-to-Many
«بهبود عملکرد» با «بهبود کارآیی» یکی نیست. پیاده سازی سیستم change tracking در حالت کلی، بدون پیاده سازی مباحث AOP غیرممکن است. بهتر است دوره‌ی مرتبطی را در سایت در این مورد مرور کنید تا کلیات بحث تشکیل Proxyها بهتر مشخص شوند (و ... تشکیل پروکسی با روش‌های مختلفی و با الگوریتم‌های متفاوتی ممکن است و مهم نیست که dynamic proxy چندسکویی باشد یا خیر؛ این مورد نام یک الگوی طراحی شیء گرا است و نه یک کتابخانه‌ی خاص). هدف من از عنوان این مسایل، اشاره به کلیات زیرساخت پیاده سازی این مباحث هست.
برای نمونه زمانیکه مقدار خاصیت شیء واکشی شده‌ای از Context را تغییر می‌دهید و سپس SaveChanges را فراخوانی می‌کنید، در این بین یک پروکسی وجود دارد (یک لایه‌ی نامرئی و حائل بین شیء اصلی و تغییراتی که قرار است به آن اعمال شوند) که به تغییرات گوش فرا می‌دهد و در نهایت صرفا یک کوئری به روز رسانی آن فیلد خاص را تولید می‌کند و نه تمام فیلدهای دیگر را. این نوع مفاهیم کلی در اینجا مدنظر هستند. یک نمونه پیاده سازی کلی این مفهوم را در اینجا می‌توانید مشاهده کنید.
همچنین EF Core 2.1 به همراه بسته‌ی Microsoft.EntityFrameworkCore.Proxies است که پیاده سازی Lazy loading را میسر کرده‌است و از Castel.Core هم استفاده می‌کند (یا همان Castle DynamicProxy که در دوره «Aspect oriented programming» مورد بررسی قرار گرفته‌است).
پاسخ به پرسش‌ها
جستجو Contains روی کلید های ترکیبی
  • هدف شما در اصل یافتن یک یا چند «شیء مشخص»، در یک جدول بانک اطلاعاتی است. اگر از EF استفاده می‌کنید، هر رکورد/شیء شما، قطعا یک Id منحصربفرد هم دارد (تا یک «شیء مشخص» را تشکیل دهد). فقط بر اساس این Id کوئری بگیرید (نه بر اساس لیست تمام ستون‌های موجود). نتیجه‌ی کار، شبیه به کوئری اولی می‌شود که نوشتید (که البته اینجا، List آن از نوع int است و یا کلا نوع Pk جدول کاربران) و فوق العاده هم سریع است.
  • اگر Idهای اشیاء موجود در لیست فوق را ندارید، باید از PredicateBuilder استفاده کنید تا بتوانید کوئری‌های Or پویایی را به ازای هر شیء، تولید کنید. الان این PredicateBuilder، جزئی از کتابخانه‌ی Gridify هم هست.

var predicate = PredicateBuilder.False<User>();

foreach(var user in customUsers)
{
   predicate = predicate.Or(u => u.FullName == user.FullName && 
                                 u.EyeColor == user.EyeColor);
}

var specificUsers = _context.Users.Where(predicate).ToList();
نظرات مطالب
مباحث تکمیلی مدل‌های خود ارجاع دهنده در EF Code first
- این خروجی SQL لاگ شده مطلب جاری (با تمام توضیحات و نگاشت‌های آن) توسط برنامه مطمئن SQL Server Profiler است:
SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Body] AS [Body], 
[Extent1].[ReplyId] AS [ReplyId]
FROM [dbo].[BlogComments] AS [Extent1]
منطقی هم هست. چون در ToList اول، کار با دیتابیس تمام و قطع می‌شود. ToList دوم سمت کلاینت اجرا می‌شود. یعنی تشکیل درخت نهایی توسط امکانات LINQ to Objects انجام می‌شود و نه هیچ کار اضافه‌ای در سمت سرور.
- اگر اینجا join اضافی پیدا کردید ... حتما مشکلی در تنظیمات نگاشت‌ها دارید.
- اگر duplicate reader دارید شاید بخاطر lazy loading سایر خواص راهبری است که تعریف کردید مانند User و EditByUser و غیره. این‌ها اگر قرار است نمایش داده شوند، پیش از ToList اول باید توسط متد الحاقی Include به صورت eager loading تعریف شوند تا lazy loading و duplicate reader نداشته باشید.
- برای فیلتر فیلدهای اضافی، پیش از ToList اول، با استفاده از Projection و نوشتن یک Select، موارد مورد نیاز را انتخاب کنید.
بازخوردهای دوره
بایدها و نبایدهای استفاده از IoC Containers
برای مواردی که در پروژه پیش میاد و مجبور هستیم تا یک نمونه کلاس بدون پارامتر رو ایجاد کنیم چه راهکاری رو پیشنهاد می‌کنید؟ چون عملاً اون کلاس نمی‌تونه وابستگی‌های خودش رو از طریق تابع سازنده اش اعلام کنه.
به عنوان مثال در کد زیر من می‌خوام یک Custom Route Constraint تعریف کنم:
routes.MapRoute(
   name: "PagesById",
   url: "Page/{id}",
   defaults: new { controller = "Route", action = "PageById", id = UrlParameter.Optional },
   constraints: new { id = new CustomPageByIdRoute() }
);
و اینجا نمی‌تونم پارامتری به کلاس CustomPageByIdRoute بدم . کلاس CustomPageByIdRoute به این صورت هست:
    public class CustomPageByIdRoute : IRouteConstraint
    {
        private readonly IUnitOfWork _uow;
        private readonly IPage _page;

        public CustomPageByIdRoute(IUnitOfWork uow, IPage page)
        {
            _uow = uow;
            _page = page;
        }

        public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            return _page.FindBy(x => x.Id == Convert.ToInt32(values[parameterName]) && x.DeletedBy == 0).FirstOrDefault() != null;
        }
    }

نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
با سلام؛ من از DistributedCacheTicketStore استفاده کردم و به ازای هر درخواست یک دستور آپدیت بر روی جدول AppSqlCache انجام میشود:
exec sp_executesql
    N'UPDATE [dbo].[AppSqlCache] SET ExpiresAtTime = (CASE WHEN DATEDIFF(SECOND, @UtcNow, AbsoluteExpiration) <= SlidingExpirationInSeconds THEN AbsoluteExpiration ELSE DATEADD(SECOND, SlidingExpirationInSeconds, @UtcNow) END) WHERE Id = @Id AND @UtcNow <= ExpiresAtTime AND SlidingExpirationInSeconds IS NOT NULL AND (AbsoluteExpiration IS NULL OR AbsoluteExpiration <> ExpiresAtTime) ;SELECT Id, ExpiresAtTime, SlidingExpirationInSeconds, AbsoluteExpiration, Value FROM [app].[SqlCache] WHERE Id = @Id AND @UtcNow <= ExpiresAtTime;',
    N'@Id nvarchar(449),@UtcNow datetimeoffset(7)',
    @Id = N'AuthSessionStore-380ca866e7744f569659a3c160eca7ef',
    @UtcNow = '2020-05-24 21:30:11.0837838 +00:00';
و زمان ترافیک سایت آپدیت این جدول باعث کندی می‌شود. آیا ایده‌ی برای حال این مشکل هست؟
نظرات مطالب
پیاده سازی JSON Web Token با ASP.NET Web API 2.x
من قبلا اینjwt رو تو یکی از پروژه هام راه اندازی کردم ومشکلی نداشتم . اما در پروژه جدیدی که ایجادکردم و بر روی پروژه این jwt رو اوردم هنگام کلیک بر روی لاگین و فراخوانی تابع dologin خطا میده که متن خطا به شکل زیر هست. 
<h2> <i>The resource cannot be found.</i> </h2></span>
<b> Description: </b>HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. &nbsp;Please review the following URL and make sure that it is spelled correctly.
 <br><br>
 
 <b> Requested URL: </b>/login<br><br>
 
 [HttpException]: The controller for path &#39;/login&#39; was not found or does not implement IController.

نظرات مطالب
Url Routing در ASP.Net WebForms
سلام
دستورات ذکر شده رو در فایل web.config قرار دادم. دسترسی به فایلهای aspx قطع شد و فقط میشد از طریق routing دسترسی داشت. اما مشکلی که بوجود اومد این بود که وقتی نام سایت رو در مرورگر وارد میکردم، خطای زیر رو دریافت کردم
The resource cannot be found.

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly. 

Requested URL: /Default.aspx
به ذهنم رسید که در فایل global دستور زیر رو وارد کنم
routes.MapPageRoute("Default", "", "~/default.aspx", False)
وقتی این کارو کردم دیدم که مشکل دسترسی به فایل default.aspx حل شد اما دسترسی مستقیم به فایلهای aspx که در فولدرهای دیگر بودند دچار مشکل شد
در وب جستجو کردم و به لینکهای روبرو رسیدم ( +  )
اما نتیجه ای نداد.
مطالب
بررسی مفهوم Event bubbling در جی کوئری و تاثیر آن بر کارآیی کدهای نوشته شده
Event bubbling یا جوشیدن رویدادها به مفهوم انتقال رویدادهای رخ داده در یک المنت به سمت المنت یا المنت‌های والد می‌باشد. برای مثال با کلیک بر روی یک المنت در صفحه، رویداد کلیک هم در همان المنت اجرا خواهد شد و هم در المنت‌های والد.
ساختار سند زیر را در نظر بگیرید:
<div id="parent">
    <div id="child1">
        <div id="child2">
            <div id="child3"></div>
        </div>
    </div>
</div>
حال اگر برای هرکدام از divهای موجود در سند، یک هندلر برای مدیریت رویداد کلیک نوشته شود و کاربر بر روی child3 کلیک کند، به ترتیب ابتدا رویداد مربوط به المنت child3 سپس child2 سپس child1 و در نهایت parent اجرا خواهد شد. یعنی با کلیک بر روی child3، تمامی هندلرهای کلیک اجرا خواهند شد. دلیل اینکار همان مفهوم Event bubbling است.
Event bubbling فقط مختص صفحات وب نیست؛ بلکه در تمامی سیستم عامل‌ها یکی از مفاهیم مدیریت رخدادها(Events) است. حتی در برنامه‌های مبتنی بر ویندوز فرم هم شما با این مفهوم برخورد کرده‌اید.
در صفحات وب، در نهایت رویدادها به شیء Window منتقل می‌شوند و در یک وب فرم، به From اصلی برنامه.
حال با این مقدمه به سراغ بهینه سازی کدهای نوشته شده‌ی خود می‌رویم. اگر از کتابخانه‌ی جی‌کوئری استفاده کرده باشید، حتما از رویدادهای مختلف ماوس و صفحه کلید بهره برده‌اید. تصور برنامه‌ای که از رویدادها استفاده نکند و باید با کاربر در تعامل باشد، غیرممکن است؛ زیرا این رویدادها هستند که درخواست‌های کاربر را به برنامه منتقل می‌کنند.
به قطعه کد زیر توجه کنید:
$('#parent').on('click', function (event) {

});

$('#child1').on('click', function (event) {

});

$('#child2').on('click', function (event) {

});
ما یک هندلر برای مدیریت رویداد کلیک المنت parent نوشته‌ایم؛ یکی برای المنت child1 و یکی دیگر برای child2. با استفاده از مفهوم جوشیدن رخدادها می‌توانیم هر سه هندلر را حذف و به یک هندلر تبدیل کنیم!
$(document).on('click', '#parent, #child1, #child2, #child3', function (event) {

});
شاید بپرسید مزیت اینکار چیست؟ نکته‌ی کلیدی در همینجاست. میزان حافظه‌ی مصرفی مدیریت یک رخداد، به مراتب کمتر از چندین رخداد است.
در واقع شما فقط یک هندلر را ثبت و تمامی کارهای لازم را به آن می‌سپارید. شدیدا توصیه می‌شود که در نوشتن کدهای خود از ایجاد هندلر بر روی هر عنصر خودداری کنید.
برای مثال اگر شما در صفحه‌ی مدیریت پست‌ها قرار دارید و برای ویرایش هر پست دکمه‌ای را تعیین کرده باشید به جای نوشتن کدی مانند زیر:
$('.post .edit').on('click', function (event) {

});
از نسخه‌ی بهینه شده‌ی آن استفاده کنید:
$(document).on('click', '.post .edit', function (event) {

});
تصور کنید شما در همین صفحه 50 پست را به کاربر نشان داده باشید و اگر از کد بالا استفاده کنید، به ازای هر 50 دکمه‌ی ویرایش، یک هندلر برای رویداد کلیک خواهید داشت. حالا اگر از کد پایین استفاده کنید، تنها یک هندلر برای 50 رویداد خواهید داشت.
همان صفحه‌ی مدیریت پست را در نظر بگیرید. 50 پست داریم. هر کدام یک دکمه‌ی ویرایش، حذف، امتیازات، کامنتها و کلی ابزار دیگر که همه با رویداد کلیک فعال می‌شوند. چیزی حدود به 300 رویداد را باید ثبت کنید!
این واقعا یک تراژدی بزرگ در مصرف حافظه محسوب می‌شود. پس بهینه‌تر است تا با نوشتن یک رویداد کلیک روی کل شیء سند، از ایجاد هندلرهای اضافی خودداری کنید.

در اینجا دو نکته قابل ذکر است:
1- چگونه از Event bubbling جلوگیری کنیم؟
برخی از اوقات لازم است تا در لایه‌های تو در تو، به ازای هر لایه، کد خاصی اجرا شود. یعنی با کلیک بر روی child3 نمی‌خواهیم رویداد مربوط به parent یا حتی child2 اجرا شوند. در این حالت باید از event.stopPropagation در بدنه‌ی هندلر استفاده کنیم.

2- چگونه می‌توان تشخیص داد که بر روی کدام لایه یا المنت کلیک شده است؟
شما با استفاده از event.event.target، به شیء هدف دسترسی خواهید داشت. برای مثال اگر قصد داشته باشیم که قسمتی از کدهای ما فقط بر روی یک المنت خاص اجرا شوند، می‌توانیم به شکل زیر آنها را تفکیک کنیم:
        var elemnt = $(event.target);
        if (elemnt.attr('id') === 'parent') {
            alert('this is parnet');
        }
        else if (elemnt.attr('id') === 'child2') {
            alert('this is child2');
        }
البته نوشتن شرط برای همه‌ی المنت‌ها در یک هندلر هم باعث طولانی شدن کدها و هم تولید کد اضافه خواهد شد. خوشبختانه جی کوئری، مدیریت و ثبت رخدادها را هوشمندانه انجام می‌دهد. به جای نوشتن شرط، به راحتی کدهای مربوط به هر المنت را در یک رجیستر کننده‌ی جدا بنویسید و در نهایت جی کوئری آن‌ها را برای شما به یک هندلر منتقل خواهد کرد:
$(document).ready(function () {

    $(document).on('click','#parent', function (event) {

    });

    $(document).on('click','#child1', function (event) {

    });

    $(document).on('click','#child2', function (event) {
        event.st
    });

});

یکی دیگر از مهمترین مزایای کدنویسی به شکل فوق اینست که حتی رویدادهای مربوط به اشیایی که به صورت پویا به سند اضافه می‌شوند، اجرا خواهند شد.
در صفحه‌ی اصلی همین سایت بر روی دکمه‌ی بارگزاری بیشتر کلیک کنید. پس از اضافه شدن پست‌ها سعی کنید به یک پست امتیاز دهید. اتفاقی نخواهد افتاد. زیرا برای عناصری که بصورت پویا به صفحه اضافه شده‌اند رویدادی ثبت نشده است، که اگر از کدهای فوق استفاده شود با کمترین هزینه به هدف دلخواه خود خواهیم رسید.
پس همیشه رویدادها را تا حد امکان بر روی عنصر ریشه تعریف کنید.
دیدن لینک زیر برای اجرای یک تست و درک بهتر مطلب خالی از لطف نخواهد بود:
http://jsperf.com/jquery-body-delegate-vs-document-delegate
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 10 - بررسی تغییرات Viewها
تا اینجا یک پروژه‌ی خالی ASP.NET Core 1.0 را به مرحله‌ی فعال سازی ASP.NET MVC و تنظیمات مسیریابی‌های اولیه‌ی آن رسانده‌ایم. مرحله‌ی بعد، افزودن Viewها، نمایش اطلاعاتی به کاربران و دریافت اطلاعات از آن‌ها است و همانطور که پیشتر نیز عنوان شد، برای «ارتقاء» نیاز است «15 مورد» ابتدایی مطالب ASP.NET MVC سایت را پیش از ادامه‌ی این سری مطالعه کنید.

معرفی فایل جدید ViewImports

پروژه‌ی خالی ASP.NET Core 1.0 فاقد پوشه‌ی Views به همراه فایل‌های آغازین آن است. بنابراین ابتدا در ریشه‌ی پروژه، پوشه‌ی جدید Views را ایجاد کنید.
فایل‌های آغازین این پوشه هم در مقایسه‌ی با نگارش‌های قبلی ASP.NET MVC اندکی تغییر کرده‌اند. برای مثال در نگارش قبلی، فایل web.config ایی در ریشه‌ی پوشه‌ی Views قرار داشت و چندین مقصود را فراهم می‌کرد:
الف) در آن تنظیم شده بود که هر نوع درخواستی به فایل‌های موجود در پوشه‌ی Views، برگشت خورده و قابل پردازش نباشند. این مورد هم از لحاظ مسایل امنیتی اضافه شده بود و هم اینکه در ASP.NET MVC، برخلاف وب فرم‌ها، شروع پردازش یک درخواست، از فایل‌های View شروع نمی‌شود. به همین جهت است که درخواست مستقیم آن‌ها بی‌معنا است.
در ASP.NET Core، فایل web.config از این پوشه حذف شده‌است؛ چون دیگر نیازی به آن نیست. اگر مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایل‌های استاتیک» را به خاطر داشته باشید، هر پوشه‌ای که توسط میان افزار Static Files عمومی نشود، توسط کاربران برنامه قابل دسترسی نخواهد بود و چون پوشه‌ی Views هم به صورت پیش فرض توسط این میان افزار عمومی نمی‌شود، نیازی به فایل web.config، جهت قطع دسترسی به فایل‌های موجود در آن وجود ندارد.

ب) کاربرد دیگر این فایل web.config، تعریف فضاهای نام پیش فرضی بود که در فایل‌های View مورد استفاده قرار می‌گرفتند. برای مثال چون فضای نام HTML Helperهای استاندارد ASP.NET MVC در این فایل web.config قید شده بود، دیگر نیازی به تکرار آن در تمام فایل‌های View برنامه وجود نداشت. در ASP.NET Core، برای جایگزین کردن این قابلیت، فایل جدیدی را به نام ViewImports.cshtml_ معرفی کرده‌اند، تا دیگر نیازی به ارث بری از فایل web.config وجود نداشته باشد.


برای مثال اگر می‌خواهید بالای Viewهای خود، مدام ذکر using مربوط به فضای نام مدل‌ها برنامه را انجام ندهید، این سطر تکراری را به فایل جدید view imports منتقل کنید:
 @using MyProject.Models

و این فضاهای نام به صورت پیش فرض برای تمام viewها مهیا هستند و نیازی به تعریف مجدد، ندارند:
• System
• System.Linq
• System.Collections.Generic
• Microsoft.AspNetCore.Mvc
• Microsoft.AspNetCore.Mvc.Rendering


افزودن یک View جدید

در نگارش‌های پیشین ASP.NET MVC، اگر بر روی نام یک اکشن متد کلیک راست می‌کردیم، در منوی ظاهر شده، گزینه‌ی Add view وجود داشت. چنین گزینه‌ای در نگارش RTM اول ASP.NET Core وجود ندارد و مراحل ایجاد یک View جدید را باید دستی طی کنید. برای مثال اگر نام کلاس کنترلر شما PersonController است، پوشه‌ی Person را به عنوان زیر پوشه‌ی Views ایجاد کرده و سپس بر روی این پوشه کلیک راست کنید، گزینه‌ی add new item را انتخاب و سپس واژه‌ی view را جستجو کنید:


البته یک دلیل این مساله می‌تواند امکان سفارشی سازی محل قرارگیری این پوشه‌ها در ASP.NET Core نیز باشد که در ادامه آن‌را بررسی خواهیم کرد (و ابزارهای از پیش تعریف شده عموما با مکان‌های از پیش تعریف شده کار می‌کنند).


امکان پوشه بندی بهتر فایل‌های یک پروژه‌ی ASP.NET Core نسبت به مفهوم Areas در نگارش‌های پیشین ASP.NET MVC

حالت پیش فرض پوشه بندی فایل‌های اصلی برنامه‌های ASP.NET MVC، مبتنی بر فناوری‌ها است؛ برای مثال پوشه‌های views و Controllers و امثال آن تعریف شده‌اند.
Project   
- Controllers
- Models
- Services
- ViewModels
- Views
روش دیگری را که برای پوشه بندی پروژه‌های ASP.NET MVC پیشنهاد می‌کنند (که Area توکار آن نیز زیر مجموعه‌ی آن محسوب می‌شود)، اصطلاحا Feature Folder Structure نام دارد. در این حالت برنامه بر اساس ویژگی‌ها و قابلیت‌های مختلف آن پوشه بندی می‌شود؛ بجای اینکه یک پوشه‌ی کلی کنترلرها را داشته باشیم و یک پوشه‌ی کلی views را که پس از مدتی، ارتباط دادن بین این‌ها واقعا مشکل می‌شود.
هرکسی که مدتی با ASP.NET MVC کار کرده باشد حتما به این مشکل برخورده‌است. درحال پیاده سازی قابلیتی هستید و برای اینکار نیاز خواهید داشت مدام بین پوشه‌های مختلف برنامه سوئیچ کنید؛ از پوشه‌ی کنترلرها به پوشه‌ی ویووها، به پوشه‌ی اسکریپت‌ها، پوشه‌ی اشتراکی ویووها و غیره. پس از رشد برنامه به جایی خواهید رسید که دیگر نمی‌توانید تشخیص دهید این فایلی که اضافه شده‌است ارتباطش با سایر قسمت‌ها چیست؟
ایده‌ی «پوشه بندی بر اساس ویژگی‌ها»، بر مبنای قرار دادن تمام نیازهای یک ویژگی، درون یک پوشه‌ی خاص آن است:


همانطور که مشاهده می‌کنید، در این حالت تمام اجزای یک ویژگی، داخل یک پوشه قرار گرفته‌اند؛ از کنترلر مرتبط با Viewهای آن تا فایل‌های css و js خاص آن.
برای پیاده سازی آن:
1) نام پوشه‌ی Views را به Features تغییر دهید.
2) پوشه‌ای را به نام StartupCustomizations به برنامه اضافه کرده و سپس کلاس ذیل را به آن اضافه کنید:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Razor;
 
namespace Core1RtmEmptyTest.StartupCustomizations
{
  public class FeatureLocationExpander : IViewLocationExpander
  {
   public void PopulateValues(ViewLocationExpanderContext context)
   {
    context.Values["customviewlocation"] = nameof(FeatureLocationExpander);
   }
 
   public IEnumerable<string> ExpandViewLocations(
    ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
   {
    return new[]
    {
      "/Features/{1}/{0}.cshtml",
      "/Features/Shared/{0}.cshtml"
    };
   }
  }
}
حالت پیش فرض ASP.NET MVC، یافتن فایل‌ها در مسیرهای Views/{1}/{0}.cshtml و Views/Shared/{0}.cshtml است؛ که در اینجا {0} نام view است و {1} نام کنترلر. این ساختار هم در اینجا حفظ شده‌است؛ اما اینبار به پوشه‌ی جدید Features اشاره می‌کند.
RazorViewEngine برنامه، بر اساس وهله‌ی پیش فرضی از اینترفیس IViewLocationExpander، محل یافتن Viewها را دریافت می‌کند. با استفاده از پیاه سازی فوق، این پیش فرض‌ها را به پوشه‌ی features هدایت کرده‌ایم.
3) در ادامه به کلاس آغازین برنامه مراجعه کرده و پس از فعال سازی ASP.NET MVC، این قابلیت را فعال سازی می‌کنیم:
public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc();
  services.Configure<RazorViewEngineOptions>(options =>
  {
   options.ViewLocationExpanders.Add(new FeatureLocationExpander());
  });
4) اکنون تمام فایل‌های مرتبط با یک ویژگی را به پوشه‌ی خاص آن انتقال دهید. منظور از این فایل‌ها، کنترلر، فایل‌های مدل و ویوومدل، فایل‌های ویوو و فایل‌های js و css هستند و نه مورد دیگری.
5) اکنون باید پوشه‌ی Controllers خالی شده باشد. این پوشه را کلا حذف کنید. از این جهت که کنترلرها بر اساس پیش فرض‌های ASP.NET MVC (کلاس ختم شده‌ی به کلمه‌ی Controller واقع در اسمبلی که از وابستگی‌های ASP.NET MVC استفاده می‌کند) در هر مکانی که تعریف شده باشند، یافت خواهند شد و پوشه‌ی واقع شدن آن‌ها مهم نیست.
6) در آخر به فایل project.json مراجعه کرده و قسمت publish آن‌را جهت درج نام پوشه‌ی Features اصلاح کنید (تا در حین توزیع نهایی استفاده شود):
"publishOptions": {
 "include": [
  "wwwroot",
  "Features",
  "appsettings.json",
  "web.config"
 ]
},


در اینجا نیز یک نمونه‌ی دیگر استفاده‌ی از این روش بسیار معروف را مشاهده می‌کنید.


امکان ارائه‌ی برنامه بدون ارائه‌ی فایل‌های View آن

ASP.NET Core به همراه یک EmbeddedFileProvider نیز هست. حالت پیش فرض آن PhysicalFileProvider است که بر اساس تنظیمات IViewLocationExpander توکار (و یا نمونه‌ی سفارشی فوق در مبحث پوشه‌ی ویژگی‌ها) کار می‌کند.
برای راه اندازی آن ابتدا نیاز است بسته‌ی نیوگت ذیل را به فایل project.json اضافه کرد:
{
  "dependencies": {
        //same as before
   "Microsoft.Extensions.FileProviders.Embedded": "1.0.0"
  },
سپس تنظیمات متد ConfigureServices کلاس آغازین برنامه را به صورت ذیل جهت معرفی EmbeddedFileProvider تغییر می‌دهیم:
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
  options.ViewLocationExpanders.Add(new FeatureLocationExpander());
 
  var thisAssembly = typeof(Startup).GetTypeInfo().Assembly; 
  options.FileProviders.Clear();
  options.FileProviders.Add(new EmbeddedFileProvider(thisAssembly, baseNamespace: "Core1RtmEmptyTest"));
});
و در آخر در فایل project.json، در قسمت build options، گزینه‌ی embed را مقدار دهی می‌کنیم:
"buildOptions": {
  "emitEntryPoint": true,
  "preserveCompilationContext": true,
  "embed": "Features/**/*.cshtml"
},
در اینجا چند نکته را باید مدنظر داشت:
1) اگر نام پوشه‌ی Views را به Features تغییر داده‌اید، نیاز به ثبت ViewLocationExpanders آن‌را دارید (وگرنه، خیر).
2) در اینجا جهت مثال و بررسی اینکه واقعا این فایل‌ها از اسمبلی برنامه خوانده می‌شوند، متد options.FileProviders.Clear فراخوانی شده‌است. این متد PhysicalFileProvider  پیش فرض را حذف می‌کند. کار PhysicalFileProvider  خواندن فایل‌های ویووها از فایل سیستم به صورت متداول است.
3) کار قسمت embed در تنظیمات build، افزودن فایل‌های cshtml به قسمت منابع اسمبلی است (به همین جهت دیگر نیازی به توزیع آن‌ها نخواهد بود). اگر صرفا **/Features را ذکر کنید، تمام فایل‌های موجود را پیوست می‌کند. همچنین اگر نام پوشه‌ی Views را تغییر نداده‌اید، این مقدار همان Views/**/*.cshtml خواهد بود و یا **/Views


4) در EmbeddedFileProvider می‌توان هر نوع اسمبلی را ذکر کرد. یعنی می‌توان برنامه را به صورت چندین و چند ماژول تهیه و سپس سرهم و یکپارچه کرد (options.FileProviders یک لیست قابل تکمیل است). در اینجا ذکر baseNamespace نیز مهم است. در غیر اینصورت منبع مورد نظر از اسمبلی یاد شده، قابل استخراج نخواهد بود (چون نام اسمبلی، قسمت اول نام هر منبعی است).


فعال سازی کامپایل خودکار فایل‌های View در ASP.NET Core 1.0

این قابلیت به زودی جهت یافتن مشکلات موجود در فایل‌های razor پیش از اجرای آن‌ها، اضافه خواهد شد. اطلاعات بیشتر
نظرات مطالب
بررسی روش مشاهده خروجی SQL حاصل از کوئری‌های Entity framework Core
یک نکته‌ی تکمیلی: روش مشاهده‌ی مقدار پارامترها در لاگ‌های SQL
در حالت معمولی، خروجی SQL لاگ شده‌ی توسط EF Core به صورت زیر است:
Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (41ms) 
[Parameters=[@__id_0='?' (DbType = Int32)], 
CommandType='Text', CommandTimeout='30']
 SELECT TOP(2) [m].[Id], [m].[Address], [m].[City], [m].[Email], [m].[Name], [m].[Phone], [m].[PostalCode], [m].[State] FROM [Contact] AS [m] WHERE [m].[Id] = @__id_0
برای مشاهده‌ی مقدار پارامترها نیاز است SensitiveDataLogging را فعال کرد:
services.AddDbContext<ContactsContext>(options => { 
  options.UseSqlServer(Configuration["Data:ContactsContext:ConnectionString"]); 
  options.EnableSensitiveDataLogging(); 
});
اینبار خروجی لاگ شده، مقدار پارامترها را نیز به همراه دارد:
Microsoft.EntityFrameworkCore.Database.Command[20100] Executing DbCommand
 [Parameters=[@__id_0='1' (Nullable = true)], 
CommandType='Text', CommandTimeout='30'] 
SELECT TOP(2) [m].[Id], [m].[Address], [m].[City], [m].[Email], [m].[Name], [m].[Phone], [m].[PostalCode], [m].[State] FROM [Contact] AS [m] WHERE [m].[Id] = @__id_0