Share

LinkedIn

Sitecore MVC Areas

Check out this Sitecore MVC extension that adds support for Areas and dependency injection with controller renderings that use fully-qualified type names.

When developing our internal standards around Sitecore MVC implementations, one of the first things we wanted was a way to keep our solution code contained and organized. In ASP.NET MVC, the convention for this is to use Areas. Unfortunately, Sitecore MVC does not yet fully support Areas out-of-the-box.

A couple of years ago, Kevin Obee showed how you can use Areas with Sitecore MVC. However, it can lead to naming conflicts between controllers and requires that you specify the full path to your views. Kevin Buckley noted that you can use fully-qualified type names to specify your controllers and, a couple of weeks later, provided new rendering type that removed the need to specify the full path for views.

There was really just one problem with Kevin’s extension. If you are using fully-qualified type names to specify your controllers, the SitecoreControllerFactory will use plain old reflection to create an instance. It does not invoke your dependency injection container. For those of us that are sticklers for best practices and SOLID design principles, this is a big problem.

As usual with Sitecore, fixing the issue is pretty easy. There are two changes that need to be made.

First, we need a controller factory that will use the DependencyResolver to create the controller instance if the controller name looks like a fully-qualified type name.  I created a FullyQualifiedSitecoreControllerFactory to do just that. This controller factory can be used instead of the default SitecoreControllerFactory by replacing the InitializeControllerFactory processor in the initialize pipeline with a different implementation.

The second problem we need to fix is that the ControllerRunner is also using reflection to create a controller instance. It should really just get out of the way and let the configured controller factory handle creating the controller instance. I modified the AreaControllerRunner that Kevin Buckley provided to do just that.

I combined the extensions above with a somewhat refactored version of Kevin Buckley’s code and made it available on GitHub. A Sitecore package is also available. Enjoy!

Sitecore development, Sitecore custom code, Sitecore MVC

Comments

Add a Comment

*
*

Please confirm you are human by typing the text you see in this image:

Chandra Prakash said: 10/28/2014 at 4:53 AM

Great post and a MVC way of doing resolution. Just to add, SitecoreControllerFactory resolves the controller in two different way, 1st one via fully qualified type name and it resolves via ControllerFactory implementation and this post moves it to IDependecyResolver.
2nd one is, If you supply the Action, Controller, Area and Namespace name in route data then SitecoreControllerFactory will hand it over to MVC ControllerFactory implementation which internally calls IDependencyResolver for resolution first and if not resolved then it will use MVC's ControllerFactory's native implementation.

I blogged about it here - <a href="http://cprakash..com/2014/10/27/sitecore-and-mvc-areas-as-pluggable-module">http://cprakash..com/2014/10/27/sitecore-and-mvc-areas-as-pluggable-module</a>

Ben Golden said: 10/28/2014 at 5:26 PM

Hi Chandra-

I just took a look at your post. That's a nice variation. Adding the namespace field/DataToken does keep it more in line with the standard ASP.NET MVC convention.

However, the reason they used that approach was so that you could use the same route pattern to match controllers from multiple assemblies. When we create controller renderings, we are essentially defining a unique route for each controller, so there isn't much point in searching multiple namespaces. It should be more efficient (though maybe not noticeably so) to just resolve a fully qualified type name.

Still, it doesn't hurt to have both options. Maybe we should combine the two approaches. What do you think?

Chandra Prakash said: 10/30/2014 at 3:19 PM

Hi Ben,

Thanks, my enhancement requires one route per MVC areas, so this ok but yes ASP.NET Convention doesn't enforce the namespace rule and it will try all possible route for resolution and resolve controller based on Area/Controller/Action name except if multiple controllers are present with same name and in that case it need namespace to resolve it. Possibly, I will update the code to avoid mandatory namespace name.

For resolution on type name Sitecore uses ControllerFactory which internally call Reflection to create controller and that limit if someone is using DI in controller constructor, else it works without any issue.

Chandra Prakash said: 10/31/2014 at 3:36 AM

Sorry for the half post earlier, yes, it makes really a good combination and free users to choose what they like, I am still working on my framework and once it is ready, I will post it on GitHub. Also, I will submit a pull request to add this option in your solution.

Josh said: 11/25/2014 at 5:39 PM

Hi Ben - I installed your package and have been trying to get everything working but just had a quick question. My rendering seems to route correctly to my controller in the correct area (with a fully qualified controller name), but the controller can't seem to find the view. I get "The view or its master was not found or no view engine supports the searched locations." and the locations listed do not include the Areas directory. In the post you mentioned Kevin's package that added a new rendering type which removed the need to specify the full path for views. Do I still need to install that along with your extension to get this functionality? Any help you could provide would be much appreciated!

Ben Golden said: 12/2/2014 at 5:37 PM

Sorry, Josh (and anyone else who tried). It looks like that package was messed up. I have corrected it and tested it on a fresh install. Please let me know if you still have issues.

Josh said: 12/5/2014 at 10:40 AM

I'm not sure if I was doing something wrong, but the package isn't adding the Area field for me. I saw it in the TDS item, so I added that field to the template and all seems to be working well now!