Friday, 14 December 2018

Custom Controller Factory in ASP.NET MVC

When ASP.NET MVC receives a request, it needs to manage how to handle it with a specific controller and the action methods in it. The component that is responsible to map an incoming request to a specific controller and decide which controller to use is controller factory. There is a default controller factory in ASP.NET MVC that maps incoming requests to a controller with a Controller postfix.

        Request  --> Routing System ---> Controller Factory ---> Invoke Controller
The built-in controller factory is the registered controller factory by default and is implemented in DefaultControllerFactory class. Also it’s possible to extend its behavior with minor changes by deriving from this base class.
But in some circumstances you may need to have a fully customized behavior for your controller factories. One common example is when you use Dependency Injection frameworks where you need to use a customized factory. Fortunately, most of the DI frameworks provide such a customized controller factory out of the box, but if you were faced with a case to implement such a factory, you can implement the IControllerFactory interface and register your own custom controller factory.
IControllerFactory is an interface with two methods:
  • CreateController: Getting the RequestContext instance and the string value of controller name, returns the controller to be used.
  • ReleaseController: Gets a controller instance and releases this controller.
Implementation of a controller factory is comparatively easy, and can be done with less amount of work to be done.
In this post I implement a basic controller factory that loads controllers based on the user’s language, so a specific controller can be loaded for a specific language. In this sample application, I define the type name of controllers in web configuration file based on a pattern that corresponds to a specific culture, and implement a controller factory that loads the appropriate controllers group based on the client’s preferences.
First I define my type patterns in my configuration file as application settings. Here I have two cultures: if the user uses Farsi, then the Farsi controllers will be loaded, otherwise the default English language will be used.
<appSettings>
  <add key="EnglishControllerTypePattern" value="IControllerFactorySample.Controllers.En.{0}"/>
  <add key="FarsiControllerTypePattern" value="IControllerFactorySample.Controllers.Fa.{0}"/>
</appSettings>

Now I write my controller factory by implementing the IControllerFactory interface and its two methods.
using System;
using System.Configuration;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace IControllerFactorySample.ControllerFactories
{
    public class CustomControllerFactory : IControllerFactory
    {
        #region IControllerFactory Members
 
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (string.IsNullOrEmpty(controllerName))
                throw new ArgumentNullException("controllerName");
 
            string language = requestContext.HttpContext.Request.Headers["Accept-Language"];
 
            string controllerType = string.Empty;
 
            if (language == "fa-IR")
                controllerType = string.Format
                    (ConfigurationManager.AppSettings["FarsiControllerTypePattern"], controllerName);
            else
                controllerType = string.Format
                    (ConfigurationManager.AppSettings["EnglishControllerTypePattern"], controllerName);
 
            IController controller = Activator.CreateInstance(Type.GetType(controllerType)) as IController;
 
            return controller;
        }
 
        public void ReleaseController(IController controller)
        {
            if (controller is IDisposable)
                (controller as IDisposable).Dispose();
            else
                controller = null;
        }
 
        #endregion
    }
}

In the CreateController function, I detect the client’s language using the HTTP headers of the request, and load the appropriate type name based on the user’s culture. Then I use reflection APIs to load the type and create an instance of the controller to be returned. Note that this implementation doesn’t mandate the Controller postfix for controller names, so rather than defining my Home controller as HomeController class, I just can use Home name. I have defined my controllers in Fa and En sub-folders inside Controllers folder, so my controller factory can load them based on the type name patterns. Besides, in the ReleaseController method, I dispose the controller as expected.
The third and last step is to add this controller factory as the default factory to ASP.NET MVC. This can be done in Global.asax and its Application_Start method where I use ControllerBuilder.SetControllerFactory to add my factory type as the default controller factory to ASP.NET MVC.
using System.Web.Mvc;
using System.Web.Routing;
using IControllerFactorySample.ControllerFactories;
 
namespace IControllerFactorySample
{
    public class MvcApplication : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
 
        }
 
        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);
 
            ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory));
        }
    }
}
As you see, building a controller factory is very straightforward and you can get it done in a few simple steps. If I run the application and set my preferred language to Farsi, then Farsi controllers will be used to serve the requests, otherwise English controllers will be loaded.



0 comments:

Post a Comment

Topics

ADFS (1) ADO .Net (2) Ajax (1) Angular (1) Angular Js (17) Angular2 (28) ASP .Net (14) Authentication (1) Azure (1) Breeze.js (1) C# (50) CD (1) CI (2) CloudComputing (1) CMS (1) CSS (2) Design_Pattern (3) DevOps (4) DI (4) Dotnet (22) Entity Framework (3) ExpressJS (4) Html (3) IIS (1) Javascript (6) Jquery (8) Lamda (3) Linq (11) Mongodb (1) MVC (50) NodeJS (7) RDLC (1) Report (1) SDLC (1) Sql Server (30) SSIS (3) SSO (1) SSRS (2) UI (1) WCF (13) Web Api (11) Web Service (1) XMl (1)