11

我们有一个 ASP.NET MVC 4 应用程序,其中包含大约 3000 个视图。我们决定将这组视图拆分为单独的 DLL,并使用 RazorGenerator 对其进行编译。我们在主 MVC 项目中只保留 main _Layout.cshtml 和相关文件。

我们无法从 DLL 加载部分视图以及主 MVC 项目中的主视图。详细说明如下。

已经做了什么:

  1. 视图成功编译成 DLL(我已经确认它们在二进制文件中)

  2. 使用 Global.asax.cs 中 Application_Start 中的以下代码为每个包含视图的 DLL 创建和注册 PrecompiledMvc​​Engine 对象:

.

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    // ...
    // some code determining whether we've got an assembly with views
    // ...

    var engine = new PrecompiledMvcEngine(assembly);
    engine.UsePhysicalViewsIfNewer = true;

    ViewEngines.Engines.Insert(0, engine);

    // StartPage lookups are done by WebPages. 
    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}

什么不起作用:

我无法加载在主 MVC 项目(例如 _Layout.cshtml)中定义的视图以及在其中一个库(例如 Partial.cshtml)中定义的部分视图。我在控制器的操作中使用以下代码来告诉 MVC 框架我请求了哪个视图:

var view = "~/Views/" + partialName + ".cshtml";    
return View(view, "~/Views/Shared/_Layout.cshtml", model);

错误消息显示:未找到视图“~/Views/Partial.cshtml”或其主视图,或者没有视图引擎支持搜索的位置。搜索了以下位置:~/Views/Partial.cshtml ~/Views/Shared/_Layout.cshtml

当我尝试通过指定以下任一方式单独加载视图时:

return View("~/Views/Shared/_Layout.cshtml", model);

或者

return View(view, model);

,找到正确的视图。但是我需要将它们一起加载。当我在主 MVC 项目中拥有所有必需的 .cshtml 文件时,该代码有效。

请注意,已编译 DLL 中的视图具有 PageVirtualPathAttribute,其路径与控制器操作中指定的路径相同,例如:

namespace SomeBaseNamespace.Views
{
    [GeneratedCode("RazorGenerator", "1.5.0.0"), PageVirtualPath("~/Views/Partial.cshtml")]
    public class Partial : WebViewPage<PartialModel>
    {
        [CompilerGenerated]
        private static class <Execute>o__SiteContainer3
        {
            // logic
        }

        public override void Execute()
        {
            // logic
        }
    }
}

综上所述,问题是如何用另一个项目中定义的部分编译视图调用存储在主 MVC 项目中的主视图?

4

3 回答 3

2

在应用程序启动时,当您的应用程序调用此行时...

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())

包含您的外部视图的程序集可能尚未加载,因此不包含在视图引擎中。我实际上建议不要使用AppDomain.CurrentDomain.GetAssemblies(),因为这将包括在启动时加载的所有程序集。

解决方案是将RazorGenerator.Mvc NuGet 包添加到每个包含已编译视图的项目中。这将以与您类似的方式添加以下应用程序启动代码...

[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(SomeBaseNamespace.Views.RazorGeneratorMvcStart), "Start")]

namespace SomeBaseNamespace.Views
{
    public static class RazorGeneratorMvcStart
    {
        public static void Start()
        {
            var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) 
            {
                UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
            };

            ViewEngines.Engines.Insert(0, engine);
        }
    }
}

请注意这是如何使用当前程序集(您的视图程序集)创建视图引擎并将其添加到静态ViewEngines集合(包含在主 MVC 项目中)。

一旦投入生产,我还建议关闭该UsePhysicalViewsIfNewer设置,这会增加显着的性能开销。

于 2016-06-06T06:15:16.750 回答
1

术语

BaseMvc - 带有 Razor 生成的视图、控制器等
ConsumerMvc - 具有此项目的布局并引用 BaseMvc

概括

在基本控制器中创建视图的交付。该视图使用通过 BaseMvc 中的 _ViewStart.cshtml 存在于 ConsumerMvc 中的布局。对于我的情况,我有不同布局的项目,因此是“指针”布局视图。我认为这是一个有用的例子。

BaseMvc 示例

我创建了一个AREA,所以我可以设置一个默认布局。

/Areas/Components/Controllers/ShoppingController.cs

public ActionResult Basket()
{
    return View();
}

/Areas/Components/Views/Shopping/Basket.cshtml

Welcome to the Basket!

/Areas/Components/Views/_ViewStart.cshtml

@{
    //-- NOTE: "Layout_Component.cshtml" do not exist in the BaseMVC project. I did not
    // experiment with having it in both projects. A tip if you do is to ensure both
    // the base and consumer _Layout_Component.cshtml files are both razor
    // generated to allow the razor generator to handle the overrride. See
    // my other SO answer linked below.
    Layout = "~/Views/Shared/_Layout_Component.cshtml";
}

代码注释中引用的链接:ASP.NET MVC 站点中的覆盖视图不起作用

ConsumerMvc 示例

/Views/Shared/_Layout_Component.cshtml

@{
    Layout = "~/Views/Shared/_Layout_ConsumerMvc.cshtml";
}
@RenderBody()

我的网址

http://www.consumermvc.example.com/Components/Shopping/Basket

于 2015-08-28T02:55:54.953 回答
0

调用时并非所有程序集都被加载Application_Start。添加额外的处理程序:

AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => 
{
    // ...
    // some code determining whether we've got an assembly with views
    // ...

    var engine = new PrecompiledMvcEngine(args.LoadedAssembly);
    engine.UsePhysicalViewsIfNewer = true;

    ViewEngines.Engines.Insert(0, engine);

    // StartPage lookups are done by WebPages. 
    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}
于 2014-01-30T12:01:10.793 回答