首页 文章

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

提问于
浏览
17

我想在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项目中调用位于单独程序集中的视图组件?

2 回答

  • 1

    Update 2017-03-09

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

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

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

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

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

    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 Feed中获取这些内容 . 我通过添加此源来完成此操作:

    enter image description here

    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

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

  • 20

    我已经对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 .

相关问题