【问题标题】:Razor Generator: how to use view compiled in a library as the partial view for master defined in main mvc projectRazor Generator:如何使用在库中编译的视图作为主 mvc 项目中定义的主视图的部分视图
【发布时间】:2023-04-04 17:40:01
【问题描述】:

我们有一个 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项目中的主视图?

【问题讨论】:

  • 您好,您能完成这项工作吗?
  • 你搞清楚了吗?我似乎遇到了完全相同的问题。

标签: razor asp.net-mvc-4 viewengine razorgenerator


【解决方案1】:

在调用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);
}

【讨论】:

  • 这会将所有加载的程序集添加为视图引擎,不建议这样做。这也不适用于包含在需要之前从未加载过的视图的程序集。
  • @TomBowers,你是对的,但这是他的问题使用的代码。最好有一个PrecompiledMvcEngine,它可以根据需要添加来自其他程序集的视图。
  • 您不能将 PrecompiledMvc​​Engine 用于多个程序集。正如我的回答一样,每个包含编译视图的单独程序集都需要一个新引擎。
【解决方案2】:

术语

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

代码注释中引用的链接:Override view in ASP.NET MVC site not working

ConsumerMvc 示例

/Views/Shared/_Layout_Component.cshtml

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

我的网址

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

【讨论】:

    【解决方案3】:

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

    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-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-11
      • 2018-01-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多