Welcome to AspAdvice Sign in | Join | Help

Autofac container and Asp.Net MVC 2.0 Areas

Areas allows you to modularize the web site logic into separate units based on the feature sets.

You can read about areas here.

In the above article Scott explains a problem "What happens if two of the projects have a controller with the same name?"

The solution is to supply the namespace attribute of the MapRoute method to differentiate between the same name controllers.

We use autofac as the DI framework at my work. autofac 1.44 doesn't support the Areas concept of the MVC 2.0 yet.

With a little bit of convention, adding that support is really simple. Here is one way of doing it.

1. While registering the routes for areas and main area, supply the namespace parameter.

MainArea route

routes.MapRoute(
               "Default",                                              
               "{controller}/{action}",
               new
               {
                   controller = "Home",
                   action = "Index"
               }, new [] {"MainAreaNameSpace"}  

 

Area1 route

context.MapRoute("Area1Route","Area1/{controller}/{action}",new
               {
                   controller="Home",
                   action="index"
               },
               new[]{"Area1"}
           );

 

2.  autofac uses an interface IControllerIdentificationStrategy to identify the controllers both at the registration time and at the creation time.  We need to implement that interface. Here is the code for our AreaControllerIdentificationStrategy

 

public class AreaControllerIdentificationStrategy : IControllerIdentificationStrategy
    {
        const string TypeNameSuffix = "Controller";
 
        public Service ServiceForControllerName(string controllerName)
        {
            if (controllerName == null)
                throw new ArgumentNullException("controllerName");
 
            if (controllerName == "")
                throw new ArgumentOutOfRangeException("controllerName");
 
            return new NamedService(controllerName.ToLowerInvariant());
        }
 
        public Service ServiceForControllerType(Type controllerType)
        {
            if (controllerType == null)
                throw new ArgumentNullException("controllerType");
 
            string fullName = controllerType.FullName;
            return ServiceForControllerName(fullName.Remove(fullName.Length-TypeNameSuffix.Length, TypeNameSuffix.Length));
        }
    }

 

 

 

Here the key method to notice is, ServiceForControllerType.  We are stripping the string “Controller” from the full name of the controller type, which will then be used by AutofacControllerModule in the registration process.  In its default implementation autofac uses the controller name with out the namespace.

 

3. At run time when the mvc framework asks for the controller by its name, autofac resolves the given name from the registered names and creates a controller using AutofacControllerFactory class. Framework calls the CreateController method of the factory and supplies the RequestContext and controllerName (without the namespace)


We need to override the CreateController method and change the controller name to include the namespace so that our identification strategy can match the registered controller names.

AreaControllerFactory

public class AreaControllerFactory : AutofacControllerFactory
   {
       public AreaControllerFactory(IContainerProvider containerProvider) : base(containerProvider)
       {
       }
 
       public AreaControllerFactory(IContainerProvider containerProvider, IControllerIdentificationStrategy controllerIdentificationStrategy) : base(containerProvider, controllerIdentificationStrategy)
       {
       }
 
       public override System.Web.Mvc.IController CreateController(System.Web.Routing.RequestContext context,string controllerName)
       {
           object namespaces;
           if(context.RouteData.DataTokens!= null && context.RouteData.DataTokens.TryGetValue("Namespaces", out namespaces))
           {
               var prefix = ((string[])namespaces)[0];
               controllerName = prefix.ToLowerInvariant() + "." + controllerName.ToLowerInvariant();
           }
           return base.CreateController(context,controllerName);
       }
   }

We are taking the namespace from the datatokens dictionary of the context RouteData and prefixing that with the given controllername.

 

4.  Lastly we need to wire up our new classes, with the AutofacControllerModule and Current ControllerFactory in Global.ascx Application_Start method

 

var areaControllerIdentificationStrategy = new AreaControllerIdentificationStrategy();
builder.RegisterModule(new AutofacControllerModule(typeof(HomeController).Assembly)
                           {
                               IdentificationStrategy = areaControllerIdentificationStrategy
                           });
containerProvider = new ContainerProvider(builder.Build());
var areaControllerFactory = new AreaControllerFactory(ContainerProvider, areaControllerIdentificationStrategy);
ControllerBuilder.Current.SetControllerFactory(areaControllerFactory);

 

Here we are simply providing the values for IdentificationStrategy property of AutofacControllerModule and AutofacControllerFactory base class.

 

That is it.  Now autofac correctly recognizes the unique controllers based on their namespaces.

Published Friday, December 04, 2009 9:29 AM by kiran chand

Comments

No Comments
Anonymous comments are disabled