Using View Renderings with Glass Mapper and compiled views

Our old friend View Renderings cropped up recently when we moved our ORM related code into it’s own Helix style foundation module. First up a short history of View Renderings in Sitecore…

Traditionally when using View renderings in Sitecore, if we didn’t want to use the built in Sitecore.Mvc.Presentation.RenderingModel model for our views we’d build a custom model and ensure Sitecore itself was aware of it by creating a Model item in /sitecore/layout/Models and fill in the Model field of the View rendering.

Without this Sitecore cannot resolve the model correctly:

So far, so old.

Being forced to add a model to Sitecore felt a bit limiting, and back in 2014 John West wrote a nice article demonstrating how to remove the need for specifying the model in Sitecore (since it is already present on the top of your view) https://community.sitecore.net/technical_blogs/b/sitecorejohn_blog/posts/determine-models-from-views-with-the-sitecore-asp-net-cms

Later on, Nat Mann ran with the idea and optimised the solution for performance: https://cardinalcore.co.uk/2015/03/24/getting-a-sitecore-model-from-the-cshtml-view/

Now we can use a custom model without the need to define the model in Sitecore. Sweet!

But what if you’re using Glass Mapper…

No problem, Glass has incorporated the GetModelFromView code into the library as of release 4.0.0.4 adding the ability to determine the model required for a View Rendering from the Cshtml file itself.

This means you can happily use your Glass models with View Renderings and again, with no Sitecore Model admin.

All straight forward, but what if your views are precompiled (as Kamsar blogs about here) and don’t actually exist on disk?

Mike Edwards has happily provided the solution; Incorporating this code into your GlassMapperScCustom class will allows Glass to probe compiled view assemblies and locate the view:

public class CompiledViewTypeFinder : IViewTypeResolver
{
        public Type GetType(string path)
        {
            ViewContext current = ContextService.Get().GetCurrent<ViewContext>();
            var partial = ViewEngines.Engines.FindPartialView(current, path);

            var type = partial.View.GetType().GetField("_viewType", BindingFlags.Instance | BindingFlags.NonPublic)
                    .GetValue(partial.View)
                as Type;

            Type baseType = type.BaseType;

            if (baseType == null || !baseType.IsGenericType)
            {
                Sitecore.Diagnostics.Log.Warn(string.Format(
                    "View {0} compiled type {1} base type {2} does not have a single generic argument.",
                    path,
                    type,
                    baseType), this);

                return typeof(NullModel);
            }

            Type proposedType = baseType.GetGenericArguments()[0];
            return proposedType == typeof(object)
                ? typeof(NullModel)
                : proposedType;
        }
}

public static partial class GlassMapperScCustom
{
        ...
        ...
        public static void PostLoad() 
        {
            GetModelFromView.ViewTypeResolver = new ChainedViewTypeResolver(
                    new IViewTypeResolver[] {
                    new CompiledViewTypeFinder(),
                    new RegexViewTypeResolver() });
        }
        ...
        ...
}

Leave a Reply

Your email address will not be published. Required fields are marked *