Question

I'm about to set out building a proof of concept app to replace an ageing web forms application.

The application is off-the-shelf with customizations on a per customer basis. The existing solution contains all the main UI, plus customized versions of pages where customers have requested them.

The lead developer has built a trick system that first checks the client directory for a version of a page and only if it doesn't find one returns the base version.

i.e. First check: Views\Client1\Home\Default

if no match found, check: Views\Base\Home\Default

My question is this; Though I'm fairly sure MVC does this out of the box but I can't work out how to get started. Can you point me in the right direction? (appropriate tutorials, keywords to Google etc).

Thanks all!

Was it helpful?

Solution

You're looking for a Custom View Engine which is similar to this Stack Overflow question. But your use case is different since it would entail differing paths depending on which tenant loaded your application.

I found http://weblogs.asp.net/imranbaloch/archive/2011/06/27/view-engine-with-dynamic-view-location.aspx which I think would fit your situation nicely.

public class MyRazorViewEngine : RazorViewEngine
{
    public MyRazorViewEngine() : base()
    {
        AreaViewLocationFormats = new[] {
            "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
        };

        AreaMasterLocationFormats = new[] {
            "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
        };

        AreaPartialViewLocationFormats = new[] {
            "~/Areas/{2}/Views/%1/{1}/{0}.cshtml",
            "~/Areas/{2}/Views/%1/{1}/{0}.vbhtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.cshtml",
            "~/Areas/{2}/Views/%1/Shared/{0}.vbhtml"
        };

        ViewLocationFormats = new[] {
            "~/Views/%1/{1}/{0}.cshtml",
            "~/Views/%1/{1}/{0}.vbhtml",
            "~/Views/%1/Shared/{0}.cshtml",
            "~/Views/%1/Shared/{0}.vbhtml"
        };

        MasterLocationFormats = new[] {
            "~/Views/%1/{1}/{0}.cshtml",
            "~/Views/%1/{1}/{0}.vbhtml",
            "~/Views/%1/Shared/{0}.cshtml",
            "~/Views/%1/Shared/{0}.vbhtml"
        };

        PartialViewLocationFormats = new[] {
            "~/Views/%1/{1}/{0}.cshtml",
            "~/Views/%1/{1}/{0}.vbhtml",
            "~/Views/%1/Shared/{0}.cshtml",
            "~/Views/%1/Shared/{0}.vbhtml"
        };
    }

    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        var nameSpace = controllerContext.Controller.GetType().Namespace;
        return base.CreatePartialView(controllerContext, partialPath.Replace("%1", nameSpace));
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        var nameSpace = controllerContext.Controller.GetType().Namespace;
        return base.CreateView(controllerContext, viewPath.Replace("%1", nameSpace), masterPath.Replace("%1", nameSpace));
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        var nameSpace = controllerContext.Controller.GetType().Namespace;
        return base.FileExists(controllerContext, virtualPath.Replace("%1", nameSpace));
    }

}

Instead of replacing the %1 with the namespace, replace it with your customer name. And then add the base path to the end of all the locations to ensure the base path is always found last.

Make sure you add the view engine to the ViewEngines.Engines collection as well.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top