ASP.NET MVC 6: view components in a separate assembly

前端 未结 3 993
伪装坚强ぢ
伪装坚强ぢ 2020-12-13 10:13

I\'d like to define view components (which are new in ASP.NET MVC 6) in a separate assembly from the MVC 6 web startup project so that I can reuse them in multiple web proje

相关标签:
3条回答
  • 2020-12-13 10:52

    As of .NetCore v3.x:

    1. [Optional] Remove Microsoft.Extensions.FileProviders.Embedded nuget package
    2. Install Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation nuget package
    3. Call .AddRazorRuntimeCompilation(), e.g: services.AddMvc().AddRazorRuntimeCompilation()
    4. Instead of
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.FileProviders.Add(new EmbeddedFileProvider(
             typeof(SampleClassInAssembly).Assembly
        ));
    });
    

    Add this:

    services.Configure<MvcRazorRuntimeCompilationOptions>(options =>
    {
        options.FileProviders.Add(new EmbeddedFileProvider(
             typeof(SampleClassInAssembly).Assembly
        ));
    });
    

    And you are good to go.

    Related github issue

    0 讨论(0)
  • 2020-12-13 10:56

    I have done some researching on Github and found that PhysicalFileProvider (link) IFileInfo GetFileInfo(string subpath) method is used by Razor engine (link) for getting real files to compile.

    Current implementation of this method

    public IFileInfo GetFileInfo(string subpath)
    {
         if (string.IsNullOrEmpty(subpath))
         {
             return new NotFoundFileInfo(subpath);
         }
    
         // Relative paths starting with a leading slash okay
         if (subpath.StartsWith("/", StringComparison.Ordinal))
         {
             subpath = subpath.Substring(1);
         }
    
         // Absolute paths not permitted.
         if (Path.IsPathRooted(subpath))
         {
             return new NotFoundFileInfo(subpath);
         }
    
         var fullPath = GetFullPath(subpath);
         if (fullPath == null)
         {
             return new NotFoundFileInfo(subpath);
         }
    
         var fileInfo = new FileInfo(fullPath);
         if (FileSystemInfoHelper.IsHiddenFile(fileInfo))
         {
             return new NotFoundFileInfo(subpath);
         }
    
         return new PhysicalFileInfo(_filesWatcher, fileInfo);
    }
    
    private string GetFullPath(string path)
    {
        var fullPath = Path.GetFullPath(Path.Combine(Root, path));
        if (!IsUnderneathRoot(fullPath))
        {
            return null;
        }
        return fullPath;
    }
    

    We can see here that absolute paths nor permitted and the GetFullPath method combines path with Root which is your main web application root path.

    So I assume that u can't open ViewComponent from other folder than the current one.

    0 讨论(0)
  • 2020-12-13 11:09

    Update 2017-03-09

    Things have changed a bit in Visual Studio 2017 using MS Build. Luckily it's much simpler. Here's how to get this to work:

    In the external assembly, add this to the csproj file:

    <ItemGroup>
       <EmbeddedResource Include="Views/**/*.cshtml" />
    </ItemGroup>
    

    In the main web project, add this NuGet package: Microsoft.Extensions.FileProviders.Embedded

    Then in Startup, add the external assembly to the list of File Providers:

        services.Configure<RazorViewEngineOptions>(options =>
        {
            options.FileProviders.Add(new EmbeddedFileProvider(
                 typeof(SampleClassInAssembly).Assembly
                 # Prior to .Net Standard 2.0
                 # typeof(SampleClassInAssembly).GetTypeInfo().Assembly
            ));
        });
    

    I'll leave the original answer below for now, in case people are still trying to get this to work with older versions of .Net Core and project.json.

    ================================================================

    Here are the steps to make this work.

    • Make sure your view structure in the components assembly is the same as your web project. Note that there was a mistake in the screenshot that I posted along with my question.
    • Register CompositeFileProvider in Startup.cs of the web project:

      services.Configure<RazorViewEngineOptions>(options =>
      {
          options.FileProvider = new CompositeFileProvider(
              new EmbeddedFileProvider(
                  typeof(BookOfTheMonthViewComponent).GetTypeInfo().Assembly,
                  "BookStore.Components"
              ),
              options.FileProvider
          );
      });
      

    Both CompositeFileProvider and EmbeddedFileProvider are new, so you'll need to get these from the aspnetvnext NuGet feed. I did this by adding this source:

    Add the dependencies in project.json:

    "Microsoft.AspNet.FileProviders.Composite": "1.0.0-*",
    "Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*",
    

    Lastly, add this to the project.json of the Components assembly:

    "resource": "Views/**"
    

    That should be enough to get this working.

    Here is a working demo: https://github.com/johnnyoshika/mvc6-view-components/tree/master

    This answer was formulated from this discussion here: https://github.com/aspnet/Mvc/issues/3750

    Update 2016-01-15 There is currently one painful problem with external view components. Any changes you make to the view cshtml file does not automatically get recompiled. Even a forced Visual Studio clean and rebuild doesn't do it. You need to change a .cs file in the components assembly in order to trigger a view recompilation, but it looks like this is something that will be corrected in the future. The reason for this problem is explained here: https://github.com/aspnet/Mvc/issues/3750#issuecomment-171765303

    0 讨论(0)
提交回复
热议问题