RazorGenerator PrecompiledMvcEngine cannot locate Partial View or Editor Templates across assemblies
Asked Answered
M

0

6

I have a large MVC 4 site that is broken into one project per MVC Area. We use RazorGenerator to pre-compile all of our views into the project assembly for deployment and use the PrecompiledMvcEngine for our ViewEngine.

I have just created a new Area that I would like to share the Shared views from another assembly, but I get an InvalidOperationException when trying to locate a Partial View and none of the DisplayTemplates or EditorTemplates appear to be found either.

I believe it is similar to the issue described in this question.

My code in the RazorGenerator App_Start is like this:

var assemblies = new List<Tuple<string, Assembly>>()
{
    Tuple.Create("Areas/Area1", typeof(ABC.Project1.AreaRegistration).Assembly),
    Tuple.Create("Areas/Area2", typeof(ABC.Project2.AreaRegistration).Assembly),
};

// Get rid of the default view engine
ViewEngines.Engines.Clear();

foreach ( var assembly in assemblies )
{
    var engine = new PrecompiledMvcEngine(assembly.Item2, assembly.Item1) {
        UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
    };

    // Allow sharing of Area1 Shares views with Area2
    if (assembly.Item1 == "Areas/Area2")
    {
        var sharedPaths = new[] { "~/Areas/Area1/Views/Shared/{0}.cshtml" };

        engine.ViewLocationFormats = engine.ViewLocationFormats.Concat(sharedPaths).ToArray();
        engine.MasterLocationFormats = engine.MasterLocationFormats.Concat(sharedPaths).ToArray();
        engine.PartialViewLocationFormats = engine.PartialViewLocationFormats.Concat(sharedPaths).ToArray();
    }

    ViewEngines.Engines.Insert(0, engine);

    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}

When I encounter a partial reference in a view within Area2 like @Html.Partial("Partials/Footer"), I get the exception. It appears that Razor is looking for the correct path

System.InvalidOperationException: The partial view 'Partials/Footer' was not found or no view engine supports the searched locations. The following locations were searched:
    ~/Areas/Area2/Views/Home/Partials/Footer.cshtml
    ~/Areas/Area2/Views/Shared/Partials/Footer.cshtml
    ~/Views/Home/Partials/Footer.cshtml
    ~/Views/Shared/Partials/Footer.cshtml
    ~/Areas/Area1/Views/Shared/Partials/Footer.cshtml
at System.Web.Mvc.HtmlHelper.FindPartialView(ViewContext viewContext, String partialViewName, ViewEngineCollection viewEngineCollection)

Looking at the source code of the PrecompiledMvcEngine, it appears that it only looks for views within the assembly.

I initially thought that the view system would look across all registered ViewEngines when trying to resolve a path, but that seems to be an incorrect assumption (and I can see why one would not do that).

Is there a way to share the views across multiple assemblies?

Update

I've worked around the issue by creating a custom version of the PrecompiledMvcEngine that takes a list of assemblies in its constructor. The core change is this:

_mappings = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

foreach (var kvp in assemblies)
{
    var baseVirtualPath = NormalizeBaseVirtualPath(kvp.Key);
    var assembly = kvp.Value;

    var mapping = from type in assembly.GetTypes()
        where typeof(WebPageRenderingBase).IsAssignableFrom(type)
        let pageVirtualPath = type.GetCustomAttributes(inherit: false).OfType<PageVirtualPathAttribute>().FirstOrDefault()
        where pageVirtualPath != null
        select new KeyValuePair<string, Type>(CombineVirtualPaths(baseVirtualPath, pageVirtualPath.VirtualPath), type);

    foreach (var map in mapping)
    {
        _mappings.Add(map);
    }
}

Any better alternative or pitfalls with this approach?

Messuage answered 23/7, 2013 at 17:5 Comment(2)
Were you able to make it work any other way? Was this change added to the PrecompiledMvcEngine in the core open-source project?Kanishakanji
Have you tried using the CompositePrecompiledMvcViewEngine? It's similar to what you're doing in your Update in that it allows for use of multiple assemblies.Sexagesima

© 2022 - 2024 — McMap. All rights reserved.