Razor Generator: how to use view compiled in a library as the partial view for master defined in main mvc project

倖福魔咒の 提交于 2019-12-03 12:12:22

At app start, when your app calls this line...

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

The assemblies containing your external views have likely not yet been loaded, and are therefore not included as view engines. I'd actually recommend against using AppDomain.CurrentDomain.GetAssemblies() anyway, as that will include all assemblies loaded at startup.

The solution is to add the RazorGenerator.Mvc NuGet package to each project which contains compiled views. This will add the following app start code in a similar manner to yours...

[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);
        }
    }
}

Note how this creates a view engine using the current assembly (your views assembly) and adds it to the static ViewEngines collection (contained within the main MVC project).

Once in production, I'd also recommend turning off the UsePhysicalViewsIfNewer setting, which adds a significant performance overhead.

Valamas

Terminology

BaseMvc - with Razor Generated Views, Controllers etc
ConsumerMvc - Has layout for this project and references BaseMvc

Summary

Create the delivery of the view in the base controller. The view uses a layout which is present in the ConsumerMvc via the _ViewStart.cshtml in BaseMvc. For my situation I had projects with differing layouts, hence the "pointer" layout view. I thought it a useful example.

BaseMvc Example

I created an AREA so I could set a default layout.

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

Link referenced in the code comment: Override view in ASP.NET MVC site not working

ConsumerMvc Example

/Views/Shared/_Layout_Component.cshtml

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

My Url

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

Not all assemblies are loaded when Application_Start is called. Add an extra handler:

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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!