ASP.NET MVC 6:在单独的程序集中查看组件

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

我想在 MVC 6 Web 启动项目的单独程序集中定义视图组件(ASP.NET MVC 6 中的新组件),以便我可以在多个 Web 项目中重用它们。示例解决方案可能如下所示:

我创建了一个新的Class库(包),并在里面创建了一个视图组件。我还按照嵌套文件夹约定创建了视图。我的 BookStore.Components 项目如下所示:

当我尝试从我的 Web 项目调用此视图组件时:

@Component.Invoke("BookOfTheMonth")

...我收到内容正文为空的 500 错误。似乎已发现 ViewComponent class,但未发现该组件的 razor 视图。

我还尝试扩展 DefaultViewComponentDescriptorProvider 以便可以发现来自 BookStore.Components 程序集的视图组件:

定义了一个 AssemblyProvider

 public class AssemblyProvider : IAssemblyProvider
    {
        public IEnumerable<Assembly> CandidateAssemblies
        {
            get
            {
                yield return typeof(AssemblyProvider).Assembly;
                yield return typeof(BookStore.Components.BookOfTheMonthViewComponent).Assembly;
            }
        }
    }

使用 Autofac 注册 AssemblyProvider

builder.RegisterType<AssemblyProvider>()
    .AsImplementedInterfaces();

builder.RegisterType<DefaultViewComponentDescriptorProvider>()
    .AsImplementedInterfaces();

我不确定是否需要注册上面的DefaultViewComponentDescriptorProvider,所以我尝试了有没有它,但是在调用视图组件的页面上仍然出现500错误。

如何调用与 MVC6 Web 项目不同的程序集中的视图组件?

我对 Github 做了一些研究,发现 PhysicalFileProvider (link) IFileInfo GetFileInfo(string subpath) method is used by Razor engine (link) 用于获取要编译的真实文件。

此方法的当前实现

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;
}

我们可以在这里看到绝对路径也不被允许,并且 GetFullPath 方法将路径与 Root 结合在一起,这是您的主要 Web 应用程序根路径。

所以我假设你无法从当前文件夹以外的其他文件夹打开ViewComponent

更新2017-03-09

在 Visual Studio 2017 年使用 MS Build 时情况发生了一些变化。幸运的是,它要简单得多。以下是如何让它发挥作用:

在外部程序集中,将其添加到 csproj 文件中:

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

在主 web 项目中,添加这个 NuGet 包: Microsoft.Extensions.FileProviders.Embedded

然后在启动中,将外部程序集添加到文件提供程序列表中:

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

我现在将在下面保留原始答案,以防人们仍在尝试使其与旧版本的 .Net Core 和 project.json.

一起使用

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

以下是完成这项工作的步骤。

  • 确保您在组件程序集中的视图结构与您的 Web 项目相同。请注意,我随问题一起发布的屏幕截图中存在错误。
  • 在web项目的Startup.cs中注册CompositeFileProvider:

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

CompositeFileProviderEmbeddedFileProvider 都是新的,因此您需要从 aspnetvnext NuGet 提要中获取它们。我通过添加此来源来做到这一点:

添加project.json中的依赖:

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

最后,将此添加到 Components 程序集的 project.json 中:

"resource": "Views/**"

这应该足以让这个工作。

这是一个工作演示: https://github.com/johnnyoshika/mvc6-view-components/tree/master

这个答案是从这里的讨论中得出的:https://github.com/aspnet/Mvc/issues/3750

更新2016-01-15 目前外部视图组件存在一个痛苦的问题。您对视图 cshtml 文件所做的任何更改都不会自动重新编译。即使是强制 Visual Studio 清理和重建也无法做到这一点。您需要更改组件程序集中的 .cs 文件才能触发视图重新编译,但看起来这将在未来得到纠正。这个问题的原因解释在这里:https://github.com/aspnet/Mvc/issues/3750#issuecomment-171765303

截至 .NetCore v3.x:

  1. [可选] 删除 Microsoft.Extensions.FileProviders.Embedded nuget 包
  2. 安装Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation nuget 包
  3. 调用.AddRazorRuntimeCompilation(),例如:services.AddMvc().AddRazorRuntimeCompilation()
  4. 而不是
services.Configure<RazorViewEngineOptions>(options =>
{
    options.FileProviders.Add(new EmbeddedFileProvider(
         typeof(SampleClassInAssembly).Assembly
    ));
});

添加这个:

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

你很高兴。

Related github issue