Simple controller dependency injection in ASP.NET MVC

Sheonarayan
Posted by in ASP.NET MVC category on for Intermediate level | Points: 250 | Views : 2317 red flag
Rating: 5 out of 5  
 2 vote(s)

This article describes how to inject an object into controllers in ASP.NET MVC. To do that we shall use either DefaultControllerFactory class or IControllerFactory interface that help us in creating custom controller factory.
Recommendation
Read WebGrid in ASP.NET MVC before this article.

Introduction

While developing real time application in ASP.NET MVC, we might come to a point where we feel the need of injecting an object into ASP.NET MVC controller instance so that we can call methods of that objects in our controller classes. By default ASP.NET MVC creates a controller using parameter less constructor and we will not be able to inject our class into it. Controller dependency injection is one way that let us do that. 

To inject our class into ASP.NET MVC controller, we can follow two approaches
  1. Overriding DefaultControllerFactory class that comes along with ASP.NET MVC Framework
  2. Implementing IControllerFactory interface methods of ASP.NET MVC

Purpose: Controller dependency injection in ASP.NET MVC

The purpose of dependency injection in MVC controller is to achieve following where we are passing the parent class to the constructor of the Controller so that we can use its methods into action methods. In this case, we are using GetName() and GetLoginDetails() method of the Parent class in DefaultControllerFactoryMethod method.

public class HomeController : Controller
    {
        private readonly IParent _iparent;
        public HomeController()
        {

        }

        public HomeController(IParent parent)
        {
            _iparent = parent;
        }

        public ActionResult DefaultControllerFactoryMethod()
        {
            ViewBag.Name = _iparent.GetName();
            ViewBag.LoginDetails = _iparent.GetLoginDetails("MyUsreName");
            return View();
        }

Solution Approach - 1 -  overriding DefaultControllerFactory to achieve dependency injection

In this approach, we create our own controller factory class by inheriting DefaultControllerFactory class that is part of ASP.NET MVC framework and override its GetControllerInstance method.

Look at below code snippet carefully, In GetControllerInstance method, we are instantiating the Parent class that is being passed to the Activator.CreateInstance method only when the controller name is "Home" otherwise the default constructor of the controller is instantiated without any parameter.

public class MyCustomDefaultControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            IParent parent = new Parent();

            // here we can find out the controller name and based on that instantiate class that inherits IParent interface and pass into the CreateInstance method
            var controllername = requestContext.RouteData.Values["controller"].ToString();

            IController controller = null;
            if (controllername == "Home")
            {
                controller = Activator.CreateInstance(controllerType, new[] { parent }) as Controller;
            }
            else
            {
                controller = Activator.CreateInstance(controllerType) as Controller;
            }
            
            return controller;
        }

        public override void ReleaseController(IController controller)
        {
            IDisposable dispose = controller as IDisposable; 
            if (dispose != null)
            {
                dispose.Dispose();
            }
        }
    }

In this case, we have just taken "Home" controller for this example, in real time scenario you can put many such controller names in the If condition or switch condition.

The ReleaseController method simply checks for the controller and if it is disposable, calls the Dispose() method.

Once we have created above controller factory class, we need to register them into Global.asax Application_Start event.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

        // register default controller factory
         ControllerBuilder.Current.SetControllerFactory(new MyCustomDefaultControllerFactory());

        }

Now, if we call the DefaultControllerFactoryMethod of the Home controller we will get the respective output. The other controller doesn't get affected by this as we have passed the instance of the Parent class only when controllerName is "Home" in the DefaultControllerFactoryMethod class declared above.

Solution Approach 2 - Implementing IControllerFactory interface methods to achieve dependency injection

Create a new class that inherits IControllerFactory interface and implement its methods. In this case we have created MyCustomControllerFactory class that inherits IControllerFactory interface.

First we have implemented CreateController method of this interface that checks for the controller name and return the controller type depending on what we have written in the switch statement.

public class MyCustomControllerFactory : IControllerFactory
    {
        
        public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            IController controller = null;
            Type type = null;
            switch (controllerName)
            {
                case "Account" :
                    type = typeof(AccountController);
                    break;
                case "Manage" :
                    type = typeof(AccountController); // change to see
                    break;
                case "Another":
                    type = typeof(HomeController);
                    break;
                default:
                    type = typeof(HomeController);
                    break;
            }

            // NOTE: can instantiate an interface or class that can be passed in the CreateInstance method
            IParent parent = new Parent();

            controller = (IController)Activator.CreateInstance(type, parent);
            return controller;
        }

        public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            System.Web.SessionState.SessionStateBehavior sessionState = new System.Web.SessionState.SessionStateBehavior();
            switch (controllerName)
            {
                case "Account":
                    sessionState =  System.Web.SessionState.SessionStateBehavior.Default;
                    break;
                case "Manage":
                    sessionState = System.Web.SessionState.SessionStateBehavior.Required;
                    break;
                case "Home":
                    sessionState = System.Web.SessionState.SessionStateBehavior.ReadOnly;
                    break;
                default:
                    sessionState = System.Web.SessionState.SessionStateBehavior.Default;
                    break;
            }
            return sessionState;
        }

        public void ReleaseController(IController controller)
        {
            IDisposable disposeMe = controller as IDisposable;
            if (disposeMe != null)
            {
                disposeMe.Dispose();
            }
        }
    }
Second, we also need to implement GetControllerSessionBehavior method that let us decide what kind of behavior we want for each controller.

Third, ReleaseController as explained above simply calls the Dispose() method of the controller.

As in case of approach 1, we will also need to register this custom controller factory class into Global.asax Application_Start event.

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

           // register the custom controller factory now 
           ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory());

        }

There are the two simple ways of implementing dependency injection in the controller of ASP.NET MVC.  Read how to create custom action invoker in ASP.NET MVV here.

Word of caution

As we can see that the purpose of injecting an object in the controllers is being served using above two approaches however in the large real time application this again might be a lot of work. So the ideal solution to implement dependency injection in ASP.NET MVC controller is to use IOC containers like Windsor Castle, Unity, Ninject etc. that we may discussion in coming articles.

Hope this article was useful, if this helps do share this to the social websites and refer dotnetfunda.com to your friends and colleagues. Do write your comments below.
Recommendation
Read Quickly listing records in Grid in ASP.NET MVC after this article.
Page copy protected against web site content infringement by Copyscape

About the Author

Sheonarayan
Full Name: Sheo Narayan
Member Level: HonoraryPlatinum
Member Status: Administrator
Member Since: 7/8/2008 6:32:14 PM
Country: India
Regards, Sheo Narayan http://www.dotnetfunda.com
http://www.snarayan.com
Ex-Microsoft MVP, Author, Writer, Mentor & architecting applications since year 2001. Connect me on http://www.facebook.com/sheo.narayan | https://twitter.com/sheonarayan | http://www.linkedin.com/in/sheonarayan

Login to vote for this post.

Comments or Responses

Posted by: Rajayadav on: 11/27/2015 | Points: 25
Nice Sir, Thanks for sharing

Login to post response

Comment using Facebook(Author doesn't get notification)