Introduction
This is the first article of Extensible point of ASP.NET MVC architecture series. In this series, I would like to describe various extensible points of ASP.NET MVC framework. We know that MVC framework is very popular in Microsoft’s web technology which gives the real idea of separation of concern. MVC framework is built to keep in mind that every software component will be bits and pieces and they will be highly de-coupled. So there is full flexibility to attach and detach those components. Similarly, those bits and pieces are highly extensible. For example, if developer thinks that some default setting of some module or component does not work for here, she always use extension and re-define it on her own to give it proper shape. Some of the extensible points of ASP.NET are in routing, in time of controller creation, in time of view creation and many more.
In this article, I am planning to talk about the extensibility in time of controller creation, how we can write and inject our own logic in time of controller initialization, with example. So let’s start to understand the controller creation mechanism.
IControllerFactory Interface
This interface is on top of controller initialization mechanism. It is located in System.Web.Mvc
namespace in class library. It contains three methods. The CreateController
method is responsible for creating an instance of controller which takes two arguments, the first one is request context which carries all the information of current HTTP request including user’s context like whether user is authenticated or not or the username. The second argument is plant string type which takes controller name. It will take the controller name in which current HTTP request wants to hit.
namespace System.Web.Mvc
{
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
}
The second method is GetControllerSessionBehavior()
and the method sets the session behavior of the environment. The third method is ReleaseController()
. As the name suggests, the purpose of the method is to dispose the controller instance after its use. The interface is implemented in ControllerFactory
class of MVC framework’s library. Now, if our application demands more control over the controller instantiation mechanism and wants to implement those methods in our way, the door is always open for us. We can implement those methods in our own Factory
class and inject the class in execution pipeline.
Implement Our Own Controller Factory from IController
Now, we will implement IControllerFactory
interface in our own way. In this example, we have created our own class called “MyControllerFactory
” and implemented IControllerFactory
. The aim of the implementation is very simple; just we want to activate one controller. Let’s understand the CreateController
method. We are taking the controller location in assembly from Web.config file, because we will use the process called reflection, then we are invoking the CreateInstance()
method of Activator
class , the method is taking the controller type means type of the controller class End of Day.
public class MyControllerFactory : IControllerFactory
{
public IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
{
string controllerType = string.Empty;
IController controller = null;
controllerType = ConfigurationManager.AppSettings[controllerName];
if (controllerType == null)
throw new ConfigurationErrorsException
("Assembly not configured for controller " + controllerName);
controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
return controller;
}
public void ReleaseController(IController controller)
{
if (controller is IDisposable)
{
(controller as IDisposable).Dispose();
}
controller = null;
}
public SessionStateBehavior GetControllerSessionBehavior
(RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
The ReleaseController()
function is implemented to dispose the controller instance as we said earlier and here is the implementation. We are keeping SessionStateBehaviour
property as default. There are other few options, which are beyond our scope. Now, we will define the controller’s location in the assembly in web.config file. Just make one entry in web.config file. The value part is nothing, but the name of the controller along with namespace.
<add key="Home" value="Authentication.Controllers.HomeController" />
The next point is the registration of our controller factory class. We have to register the class to inject our execution logic in MVC pipeline. Just add the below line in Application_Start()
event of Global.asax page.
ControllerBuilder.Current.SetControllerFactory
(typeof(Authentication.Controllers.MyControllerFactory));
Fine, we are almost done, now just define the below controller in application. Once you run the application and want to reach the Home controller, you will see the execution flow has passed through our custom controller factory class. Here is the implementation of Home controller.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Inject Dependency from Controller Factory
Ok, we have seen the injection of custom login in time of controller creation, now we will implement few real time scenarios where we can implement and use the custom controller factory. Let’s think about dependency in controller. If there is dependency in controller and if we use constructor injection to resolve the dependency by passing as an argument of constructor, then we may need to control factory class. We know that one of the default criteria of controller class is parameter less constructor, but in our below example we are resolving the Home controller dependency through constructor injection and for that we need to pass the dependent object in time to Home controller instantiation. To solve this issue, we will implement our own controller factory and will inject in execution pipeline. Have a look at the below code.
public class LogService
{
public void CallLog()
{
}
}
public class HomeController : Controller
{
LogService log = null;
public HomeController(LogService _serviceTemp)
{
log = _serviceTemp;
}
public ActionResult Index()
{
return View();
}
}
In code, we are seeing that the Home controller is dependent on LogService
object and we have to pass the instance of LogService
class. So, let’s modify the CreateController()
method of MyControllerFactory
class like below:
public IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
{
string controllerType = string.Empty;
IController controller ;
controllerType = ConfigurationManager.AppSettings[controllerName];
if (controllerType == null)
throw new ConfigurationErrorsException("Controller not found " + controllerName);
controller = Activator.CreateInstance
(Type.GetType(controllerType),new HomeController(new LogService())) as IController;
return controller;
}
We are seeing that the LogService
object is passing as an argument of HomeController()
constructor and once we want to reach to Home controller, we will see that the dependent object has populated within constructor of Home controller.
Though the example is just to show the workflow, in reality we should use IoC container to resolve the dependency. Here I am talking about Dependency injection in MVC controller.
Check Authentication in Controller Factory
This is another real time situation where we can implement our own controller factory. The basic aim of the controller factory is to check whether the current HTTP request is authenticated request or not? If the request is authenticated request, then it will be redirected to appropriate controller and if the request is not authenticated then it will redirect to Notauthenticated controller. Here is the simple implementation.
public IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
{
string controllerType = string.Empty;
IController controller ;
controllerType = ConfigurationManager.AppSettings[controllerName];
if (controllerType == null)
throw new ConfigurationErrorsException("Controller not found " + controllerName);
if(requestContext.HttpContext.User.Identity.IsAuthenticated)
controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
else
controller = Activator.CreateInstance(typeof(NotAuthenticatedController)) as IController;
return controller;
}
And here is the final output.
As the checking is performed in root level of controller creation, it’s very useful to such logic which needs to perform for each and every controller like Dependency injection or security checking, if all controllers or a group of controllers demands that.
Border Line
I hope you have understood the concept of controller initialization using custom controller factory class. In our next article, we will discuss another extensible point of MVC framework.