Sitecore Support for MVC Areas

Multitenancy with MVC and Areas

When developing for Sitecore, you must always be conscious of those “other” people … that is, other tenants.

As mentioned countless times before, Sitecore offers support for multiple tenants from a single running instance, but it does not provide process or filesystem isolation for assets developed for each tenant website.

If you’re fortunate enough to be using MVC as your presentation technology of choice, MVC areas are an obvious choice for organizing and separating assets belonging to each tenant, or for breaking up a large single tenant project into multiple modules. But MVC Areas actually do far more than just organize your files into separate directories. Runtime resolution of assets based on directory convention is also great, and support for this by Visual Studio IntelliSense is even better.

Two examples of this are resolving views from a controller action, and the use of DisplayTemplates and EditTemplates.

Controller Actions

When using controllers and action methods within MVC, your return value can take many forms. Specifically, when returning a View or PartialView from the controller action, you can use the syntax:

return PartialView();

When a controller is called using one of these return values, MVC will use a convention to find the related view or partial view to render the result of the action. If the solution is not using areas, the default directory where MVC will look for the related view files includes:

And, as mentioned, IntelliSense will inspect the code and make sure that the view requested does in fact exist at one of the specific locations.

However, the same syntax will find the view within the area if the controller and route to the controller are contained within the area. When the view and controller actions methods are contained within an area, the MVC engine will first look within the area to resolve the views.

Yes – there is also a version of the PartialView(“~/Views/SomeViewName.cshtml”) methods that will allow you to specify WHERE to find the view file, but we don’t like that since it introduces magic strings into the code. However, this workaround does not work when using DisplayTemplates and EditTemplates.

DisplayTemplates and EditTemplates

Display and Edit Templates give you a way to create small partial views that are designed to render only a specific model type. These are very useful if you have some small data objects that need to be rendered in a consistent way. To use a Display Template, you can use some simple syntax within your view

@Html.DisplayFor( x => x.PropertyName)

Razor will “inspect” the type of the property and pass the item as model to a rendering that matches the type name. The same syntax works for collections – and will call the display template for each item in the collection when referenced by the collection.

The problem with display templates is that it only looks in very specific areas for the template, and there isn’t a way to override the location. If there is no current area, the display templates are expected to be in a folder

Why Doesn’t this Work in Sitecore?

In Sitecore, we don’t always use routes to get to a controller action – and we also do not use the @Html.Partial() or @Html.RenderAction() helper functions to execute views. Rather, the Sitecore ItemResolver will resolve the item we are trying to display, and then that item will be rendered – which means that it’s layout will be rendered through Sitecore’s normal pipelined process.

Of the renderings that are within the layout field of that item, there might be several components bound to the page that are view renderings or controller renderings. These renderings are rendered by Sitecore through the RenderRenderings pipeline, (rrrrrrr….), and Sitecore is not using the area of each item when executing each rendering context.

So What Can We Do?

What we need to do is two things – first, tell view and controller renderings in Sitecore that they are contained within an area. Then, tell MVC to set the area name while each rendering is being executed.

For the first part, we will add a processor within the mvc.renderRendering pipeline:

Since there is no built-in, obvious way to get the area for all rendering types (controller renderings, view renderings, layouts, etc.), we choose to use this new pipeline processor in mvc.renderingRendering to create and execute a “micro-pipeline” (as Alex Shyba would say)…

Finding the Area Name for Controller Renderings

Since controller renderings do not have a rendering path, this method will not work. For a controller rendering, we chose another approach. First, we create a template called Rendering Folder with Area Name that is derived from Rendering Folder and adds a single property – Area Name – as a text field.

To use this method, replace a rendering folder in your Sitecore path under /sitecore/Layout/Renderings with your new folder type, and set the area name field in the folder content tab.

The code within the second processor, GetAreaByRenderingFolder, will find the immediate parent of a controller rendering and read the property

— and that’s it. I have a packaged release of this added to the Sitecore Marketplace awaiting publishing … and the code is available to download and inspect on the BrainJocks GitHub page at https://github.com/brainjocks/SitecoreMvcAreas … Enjoy!

[…] does the same thing for earlier versions of Sitecore 8. They went a different route and used an MVC Areas module by Brainjocks (since there was no OOTB Area support in Sitecore at the time remember). I highly […]