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

时间:2015-12-12 06:01:30

标签: c# asp.net-core asp.net-core-mvc asp.net-core-viewcomponent

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

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

我创建了一个新的类库(Package)并在其中创建了一个视图组件。我还在嵌套文件夹约定之后创建了视图。我的BookStore.Components项目如下所示:

enter image description here

当我尝试从我的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项目位于单独程序集中的视图组件?

3 个答案:

答案 0 :(得分:22)

更新2017-03-09

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

在外部程序集中,将其添加到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项目相同。请注意,我在问题中发布的屏幕截图中存在错误。
  • 在网络项目的CompositeFileProvider中注册Startup.cs

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

CompositeFileProviderEmbeddedFileProvider都是新的,因此您需要从aspnetvnext NuGet Feed中获取这些内容。我这样做是通过添加这个来源:

enter image description here

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

答案 1 :(得分:1)

我对Github进行了一些研究,发现Razor引擎(link)使用PhysicalFileProviderlinkIFileInfo 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

答案 2 :(得分:0)

.NetCore v3.x 起:

  1. [可选]删除 it("Should NOT render WebFeatures in small screen", () => { jest.spyOn(window.screen, "width", "get").mockReturnValue(1000); const wrapper = mount(<YourComponent />); expect(wrapper.find("WebFeatures").exists()).toBe(false); }); it("Should render WebFeatures in large screen", () => { jest.spyOn(window.screen, "width", "get").mockReturnValue(1001); const wrapper = mount(<YourComponent />); expect(wrapper.find("WebFeatures").exists()).toBe(true); }); nuget程序包
  2. 安装Microsoft.Extensions.FileProviders.Embedded nuget程序包
  3. 致电Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation,例如:.AddRazorRuntimeCompilation()
  4. 代替
services.AddMvc().AddRazorRuntimeCompilation()

添加此内容:

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

你很好。

Related github issue