20

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

  • BookStore.Components (包含通用视图组件)
  • BookStore.Web1 (参考 BookStore.Components)
  • BookStore.Web2 (参考 BookStore.Components)

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

在此处输入图像描述

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

@Component.Invoke("BookOfTheMonth")

...我收到一个 500 错误,内容为空。似乎发现了 ViewComponent 类,但没有发现该组件的剃刀视图。

我还尝试扩展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 项目分开的程序集中的视图组件?

4

3 回答 3

23

2017-03-09 更新

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

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

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

在主 Web 项目中,添加此 NuGet 包: Microsoft.Extensions.FileProviders.Embedded

然后在 Startup 中,将外部程序集添加到 File Providers 列表中:

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

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

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

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

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

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

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

在此处输入图像描述

在中添加依赖项project.json

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

最后,将其添加到project.json程序Components集:

"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

于 2015-12-18T17:17:02.050 回答
1

我对 Github 进行了一些研究,发现Razor 引擎 ( linkPhysicalFileProvider ) 使用( link )方法来获取要编译的真实文件。IFileInfo GetFileInfo(string subpath)

此方法的当前实现

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从当前文件夹以外的其他文件夹打开。

于 2015-12-12T09:23:51.467 回答
1

.NetCore v3.x 开始

  1. [可选] 删除Microsoft.Extensions.FileProviders.Embeddednuget 包
  2. 安装Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilationnuget包
  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
    ));
});

你很高兴。

相关的github问题

于 2020-04-11T15:05:40.217 回答