ارتقاء به 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 پیش از اجرای آن‌ها، اضافه خواهد شد. اطلاعات بیشتر
  • #
    ‫۸ سال و ۲ ماه قبل، دوشنبه ۲۸ تیر ۱۳۹۵، ساعت ۱۴:۰۶
    روش فعال سازی منوی Controllers -> Add -> Controller

    به فایل project.json، وابستگی‌های CodeGeneration.Tools را اضافه کنید:
    {
        "dependencies": {
            // ...
            "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
                "version": "1.0.0-preview2-final",
                "type": "build"
            },
            "Microsoft.VisualStudio.Web.CodeGenerators.Mvc": {
                "version": "1.0.0-preview2-final",
                "type": "build"
            }
        },
    
        "tools": {
            // ...
            "Microsoft.VisualStudio.Web.CodeGeneration.Tools": {
                "version": "1.0.0-preview2-final",
                "imports": [
                    "portable-net45+win8"
                ]
            }
        }
    }
    پس از ذخیره سازی فایل و restore آن‌ها، با کلیک راست بر روی پوشه‌ی Controllers، منوی Add -> Controller ظاهر می‌شود:

  • #
    ‫۸ سال و ۲ ماه قبل، سه‌شنبه ۵ مرداد ۱۳۹۵، ساعت ۱۷:۱۴
    یکی دیگر از امکانات ViewImports_ :
    که شما می‌توانید در هر پوشه View مربوط به هر کنترل یک ViewImports_ اختصاصی داشته باشید
    برای مثال :

    مسیر‌های زیر را در در پوشه Views در نظر بگیرید:


    Views/_ViewImports با محتوای زیر : (موجود در ریشه پوشه Views )

    @using Core1RtmTestResources   .Web
    @using Core1RtmTestResources   .DomainLayer.Models
    @using Microsoft.AspNetCore.Identity
    @using Microsoft.AspNetCore.Mvc.Localization
    
    @using Core1RtmTestResources .Web.ViewModels.AccountViewModels //1 Move to Views/Account/_viewImports 
    
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

    و  Views/Account که در این مسیر ViewImports_ اختصاصی در نظر گرفته شده است که می‌توان قطعه کد شماره 1 مشخص شده در کد بالا را به این فایل انتقال داد

    @using Core1RtmTestResources .Web.ViewModels.AccountViewModels
    مزایا:
    وظیفه هر ViewImports_ مشخص می‌شود و بار از ViewImports_  موجود در ریشه Views کم شده و به اصطلاح کد تمیز‌تری خواهید داشت.


    و اما طی بررسی هایی که انجام دادم هنوز نتونستم متغیر(های) سراسری در ViewImports_ موجود در مسیر Views/Account تعریف کنم که در View‌های موجود در همین مسیر قابل دسترسی باشد. به قطعه کد زیر توجه فرمایید :

    @using Core1RtmTestResources.Web.ViewModels.AccountViewModels
    @inject IHtmlLocalizerFactory HtmlLocalizerFactory
    @{
        var localizer = HtmlLocalizerFactory.Create(
                    baseName: "Controllers.TestLocalController" /*مشخصات کنترلر جاری*/,
                    location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
    
        var sharedLocalizer = HtmlLocalizerFactory.Create(
                    baseName: "SharedResource" /*مشخصات*/,
                    location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
    }
    متغیر‌های localizer و sharedLocalizer در View‌های موجود در مسیر Views/Account غیر قابل دسترسی می‌باشند در حال حاضر فقط امکان تعریف این حالت در هر View به صورت مجزا راه حل پیشنهادی می‌باشد اما باعث ایجاد قطعه کد تکراری و حتی زمانبر بودن در توسعه و تغییرات می‌باشد.
    • #
      ‫۸ سال و ۲ ماه قبل، سه‌شنبه ۵ مرداد ۱۳۹۵، ساعت ۱۸:۰۹
      در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 11 - بررسی بهبودهای Razor» قسمت «نحوه‌ی سفارشی سازی کلاس پایه‌ی تمام Viewهای برنامه و معرفی inherits@ »، این مورد را توضیح دادم. برای نمونه اگر به فایل Views\_ViewStart.cshtml مراجعه کنید، یک چنین مقدار دهی عمومی در آن هست:
      @{
          Layout = "_Layout";
      }
      این خاصیت عمومی Layout در کلاس پایه‌ی تمام ویووها تعریف شده‌است. اگر می‌خواهید معادل آن‌را داشته باشید، باید یک کلاس سفارشی پایه را با ارث بری از RazorPage ایجاد کنید:
      using System.Threading.Tasks;
      using Microsoft.AspNetCore.Mvc.Localization;
      using Microsoft.AspNetCore.Mvc.Razor;
      using Microsoft.AspNetCore.Mvc.Razor.Internal;
      
      namespace Core1RtmTestResources.StartupCustomizations
      {
          public abstract class MyCustomBaseView<TModel> : RazorPage<TModel>
          {
              //روش خاص تزریق وابستگی‌ها در فایل ویژه‌ی جاری
              [RazorInject]
              public IHtmlLocalizerFactory MyHtmlLocalizerFactory { get; set; }
      
              public IHtmlLocalizer MySharedLocalizer => MyHtmlLocalizerFactory.Create(
                  baseName: "SharedResource" /*مشخصات*/,
                  location: "Core1RtmTestResources.ExternalResources" /*نام اسمبلی ثالث*/);
      
              public bool IsAuthenticated()
              {
                  return Context.User.Identity.IsAuthenticated;
              }
      
      #pragma warning disable 1998
              public override async Task ExecuteAsync()
              {
              }
      #pragma warning restore 1998
          }
      }
      در اینجا چند نکته مهم هستند:
      - در کلاس پایه‌ی سفارشی، امکان تزریق وابستگی‌ها در سازنده‌ی کلاس وجود ندارد. اما از طریق ویژگی RazorInject می‌توان این‌کار را انجام داد.
      - RazorInject نیاز به نصب وابستگی ذیل را دارد:
      {
          "dependencies": {
              //same as before
              "Microsoft.AspNetCore.Mvc.Razor": "1.0.0"
          }
      }
      - پس از تعریف این کلاس پایه، برای معرفی آن به فایل Views\_ViewImports.cshtml مراجعه کنید:
      @using Core1RtmTestResources
      @using Microsoft.AspNetCore.Mvc.Localization
      @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
      
      @inherits Core1RtmTestResources.StartupCustomizations.MyCustomBaseView<TModel>
      اکنون در تمام ویووهای برنامه به خاصیت عمومی MySharedLocalizer واقع در کلاس پایه‌ی سفارشی برنامه، دسترسی خواهید داشت:
      MySharedLocalizer from MyCustomBaseView: @MySharedLocalizer["About Title"]
    • #
      ‫۷ سال و ۱ ماه قبل، شنبه ۲۱ مرداد ۱۳۹۶، ساعت ۲۱:۲۴
      با سلام و احترام
      آیا در این روش استفاده از Area به این شکل صحیح است ؟
      public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
              {
                  // {0} - Action Name
                  // {1} - Controller Name
                  // {2} - Area Name
      
                  if (context.ActionContext.RouteData.Values.TryGetValue("area", out _))
                  {
                      return new[]
                      {
                          "/Areas/{2}/Features/{1}/{0}.cshtml",
                          "/Areas/{2}/Features/Shared/{0}.cshtml",
                          "/Features/Shared/{0}.cshtml"
                      };
                  }
                  else
                  {
                      return new[]
                      {
                          "/Features/{1}/{0}.cshtml",
                          "/Features/Shared/{0}.cshtml"
                      };
                  }
              }
      و یا باید کلا Area را به زیر مجموعه فولدر Features واقع در Root پروژه منتقل کرد که البته این حالت در مورد Area‌های کوچک توصیه شد ولی در حالتی که Area دارای کنترل‌های بسیار است استانداردی مشخص نیست.
      قطعه کد بالا برداشت بنده از لینک زیر است:
      ASP.NET Core - Feature Slices for ASP.NET Core MVC  
  • #
    ‫۷ سال و ۶ ماه قبل، دوشنبه ۷ فروردین ۱۳۹۶، ساعت ۰۰:۱۱
    به روز رسانی
    با حذف فایل project.json در VS 2017، اکنون با کلیک راست بر روی گروه نام پروژه (فایل csproj)، گزینه‌ی Edit آن ظاهر شده و مداخل ذکر شده‌ی در مطلب فوق، چنین تعاریفی را پیدا می‌کنند:  
    <Project Sdk="Microsoft.NET.Sdk.Web">
      <ItemGroup>
        <EmbeddedResource Include="Features\**\*.cshtml" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
        <None Update="wwwroot\**\*">
          <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
        </None>
      </ItemGroup>
    
      <ItemGroup>
         <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="1.1.0" />
      </ItemGroup>
    
      <ItemGroup>
        <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="1.0.0" />
      </ItemGroup>
    </Project>
    • #
      ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۲۳ مهر ۱۳۹۶، ساعت ۱۴:۵۱
       <None Update="wwwroot\**\*">
            <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
          </None>
      در مورد این خط کد یه توضیح می‌دهید که کاربردش چیه؟
      • #
        ‫۶ سال و ۱۱ ماه قبل، یکشنبه ۲۳ مهر ۱۳۹۶، ساعت ۱۵:۱۴
        CopyToPublishDirectory سه مقدار زیر را می‌تواند داشته باشد:
        Always: همیشه همه چیز را به پوشه‌ی Publish کپی می‌کند.
        PreserveNewest: فقط جدیدترین‌ها را به پوشه‌ی Publish کپی می‌کند.
        Never: چیزی را کپی نمی‌کند.
  • #
    ‫۷ سال و ۱ ماه قبل، شنبه ۲۸ مرداد ۱۳۹۶، ساعت ۱۶:۵۳
    ارتقاء به C# 7.1 و ASP.NET Core 2.0

    اگر در فایل‌های Razor برنامه‌های ASP.NET Core 2.0 می‌خواهید از قابلیت‌های C# 7.1 استفاده کنید، نیاز است تنظیم LangVersion ذیل را به فایل csproj اضافه نمائید:
      <PropertyGroup>
        <TargetFramework>netcoreapp2.0</TargetFramework>
         <LangVersion>latest</LangVersion>
      </PropertyGroup>
  • #
    ‫۶ سال و ۷ ماه قبل، یکشنبه ۸ بهمن ۱۳۹۶، ساعت ۱۶:۲۵
    نکته تکمیلی:
    در صورت استفاده از شیوه Feature Folder Structure و نرم افزار Resharper، متوجه خواهید شد که Resharper مسیر View‌ها را به درستی تشخیص نمی‌دهد و در همان مسیر پیش فرض MVC به دنبال آنها می‌گردد.
    برای رفع این مشکل ابتدا بسته
    JetBrains.Annotations

    را از طریق Nuget دریافت و نصب نموده و سپس در فایل FeatureLocationExpander و در بالای فضای نامی مقادیر زیر را بسته به نیاز خود وارد نمایید.

    using JetBrains.Annotations;  
    [assembly: AspMvcViewLocationFormat("~/Features/{1}/{0}.cshtml")] [assembly: AspMvcViewLocationFormat("~/Features/Shared/{0}.cshtml")] [assembly: AspMvcPartialViewLocationFormat("~/Features/{1}/{0}.cshtml")] [assembly: AspMvcPartialViewLocationFormat("~/Features/Shared/{0}.cshtml")]
    از این پس نرم افزار Resharper می‌داند که View‌ها و Partial View‌ها را در کدام مسیرها باید جستجو کند.
    لازم به ذکر است در صورت وجود Area می‌توان به طور مثال مقادیر زیر را نیز علاوه بر موارد ذکر شده وارد نمود.
    [assembly: AspMvcAreaViewLocationFormat("~/Areas/{2}/Features/{1}/{0}.cshtml")]
    [assembly: AspMvcAreaViewLocationFormat("~/Areas/{2}/Features/Shared/{0}.cshtml")]
    [assembly: AspMvcAreaViewLocationFormat("~/Features/Shared/{0}.cshtml")]
    [assembly: AspMvcAreaPartialViewLocationFormat("~/Areas/{2}/Features/{1}/{0}.cshtml")]
    [assembly: AspMvcAreaPartialViewLocationFormat("~/Areas/{2}/Features/Shared/{0}.cshtml")]
    [assembly: AspMvcAreaPartialViewLocationFormat("~/Features/Shared/{0}.cshtml")]
    موفق باشد.