-->

30/10/2011

Custom Controller Factory & Structure Map - MVC Razor


Oww.... wait, why i have to go for Custom Controller Factory ? 
Error shown in earlier post "Default Controller Factory Vs Custom Controller Factory"  is lack of default constroctor. So, i will add one parameter-less constructor. That is it. Problem solved. :)

Sorry, now you have created a bigger problem. Let me show you how.
If you understood the article "Dependency Injection", you will observe the flexibility (Loosely coupled) we achieved because of that.

Now, by creating a default constructor, your code will become like this...
namespace TestDI
{
    public class ClassA
    {
        private ClassB objB;

        public void getDependencyObject()
        {
            objB = new ClassB();
        }
        public ClassA()
        {           
        }

        public void ExecuteMethod()
        {
            objB.ExecuteMethod();
        }
    }

    public class ClassB
    {
        public void ExecuteMethod()
        {
        }
    }
}
Now, you have 4 problems,
1. Dependent object is tightly coupled in ClassA, which will increase the maintenance cost.
2. If user triggered ClassA.ExecuteMethod() before ClassA.getDependencyObject(), gone. Your program will end up in exceptions.
3. If you have both Default and Parametrized constructor, you are not sure which one will be used by Client code.
4. Now its hard to do Unit test against ClassA, the only possible way is to use reflection and populate private fields but that is an ugly solution.

Finally, look at the flexibility in below code:
namespace TestDI
{
    public class ClassA
    {
        private ClassB objB;
        public ClassA(ClassB objClassB)
        {
            objB = objClassB;
        }
        public void ExecuteMethod()
        {
            objB.ExecuteMethod();
        }
    }

    public class ClassB
    {
        public void ExecuteMethod()
        {
        }
    }
}
So, you will loose the main theme of  Dependency Injection (IoC), in order to fix small issue of "Parameter-less Constructor".

As we are clear why we need to go for Custom Constructor Factory, lets begin.
Current piece of code implementing DI :
namespace MVCTestDefaultControllerFactory.Controllers
{
    public class HomeController : Controller
    {
        DependencyClass objDep;
        public HomeController(DependencyClass objDepedencyObject)
        {
            objDep = objDepedencyObject;
        }
        public ActionResult Index()
        {
            ViewBag.Message = "Welcome to ASP.NET MVC!";

            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }
    public class DependencyClass
    {
    }
}
Output :
There is 2 ways of work this out.

#1. Custom Controller Factory:
In this approach, we need to create a CustomControllerFactory  implementing DefaultControllerFactory.
In order to do that we need to override two methods.
                   i. CreateController()
                  ii. ReleaseController()
Here is my CustomControllerFactory :
namespace MVCTestDefaultControllerFactory.Controllers
{
    public class MyControllerFactory : DefaultControllerFactory
    {        
        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (controllerName == null) return null;

            if (controllerName == "Home")
            {
                return new HomeController(new DependencyClass());
            }

            // check for the different controller type and then inject with the dependencies
            // too much work right!

            return null;
        }
        public override void ReleaseController(IController controller)
        {
            var disposable = controller as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
    }
}
Next step will be registering the CustomControllerfactory in place of default factory.
This can be done in Global.asax.cs, Application_Start() method.
protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
        }
Now, execute and see the result :

This is the best method, of you have less complex dependencies and less no of DI instances.
Because, you have to implement both the CreateController() method for all the controllers implementing DI.
Best part is less code modification & Better maintainability.

#2. Using Structure Map to resolve dependencies:
StructureMap is an open source Dependency Injection framework for the .NET platform and has been in use since 2004 .StructureMap supports both setter and constructor injection and also offers testing and diagnostic features such as logging, tracing and wiring of mock objects.
Download the dll and add it to local library folder.
Now, create a CustomControllerFactory as per StructureMap approach.
Below is my factory code :
namespace MVCTestDefaultControllerFactory.Controllers
{
    public class StructuremapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null) return null;
            return ObjectFactory.GetInstance(controllerType) as IController;
        }
                
        public override void ReleaseController(IController controller)
        {
            var disposable = controller as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }
    }
}
We also need to tell our application to use StructuremapControllerFactory as the default ControllerFactory. This is implemented inside the Application_Start event of the application as shown below:  
protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
            //Uncomment below 2 lines for Structuremap approach.
            StructureMapConfiguration();
            // use the new controller builder
            ControllerBuilder.Current.SetControllerFactory(new StructuremapControllerFactory());
            
            // Uncomment below line in case of "Custom factory"
            //ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
        }
If you are dealing with several dependencies then it is a good idea to use the Scan method of StructureMap.
The Scan method is responsible for parsing through the assembly and registering the assemblies according to the convention specified by the user. If there is no convention specified then the default convention is used. The default convention registers all the interfaces and their concrete types according to the naming convention.
private void StructureMapConfiguration()
        {
            ObjectFactory.Initialize(InitializeUsingScanning);
        }

        private void InitializeUsingScanning(IInitializationExpression obj)
        {
            obj.Scan(
            x =>
            {
                x.Assembly("MVCTestDefaultControllerFactory");
                x.WithDefaultConventions();
            }

            );
        }
The above code will scan the "MVCTestDefaultControllerFactory" assembly and registers all the dependencies. You do not have to register the dependencies manually.

Run the application and here is the output :

Thus we resolved one of the design level issue with MVC - IoC, in two different methods.

Is it helpful for you? Kindly let me know your comments / Questions.

No comments:

Post a Comment