ASP.NET MVC 5 Controller Extensibility
Creating a Custom Controller Factory
Create a custom controller factory by implementing IControllerFacotry interface and register it in Application_Start method of Global.asax file.
public class TestCustomControllerFactory: IControllerFactory { public IController CreateController(RequestContext requestContext, string controllerName) { Type targetType = null; switch (controllerName) { case "Test": targetType = typeof(TestController); break; default: requestContext.RouteData.Values["controller"] = "Test"; targetType = typeof(TestController); break; } return targetType == null ? null : (IController)DependencyResolver.Current.GetService(targetType); } public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) { return SessionStateBehavior.Default; } public void ReleaseController(IController controller) { IDisposable dispose = controller as IDisposable; if (dispose != null) { dispose.Dispose(); } } }
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new Infrastructure.TestCustomControllerFactory()); }
Working with the Built-in Controller Factory
DefaultControllerFactory is the built-in controller facotry class, it maintains a list of classes which meet following criteria to create controller instance:
- The class must be public.
- The class must be concrete (not abstract).
- The class must not take generic parameters.
- The name of the class must end with Controller.
- The class must implement the IController interface.
This is convension-over-configuration pattern.
Prioritizing Namespaces
Customizing DefaultControllerFactory Controller Instantiation
There are a number of ways to customize how the DefaultControllerFactory class instantiates controller objects.
Using the Dependency Resolver
The DefaultControllerFacotry call IDependencyResolver.GetService to create controller instance, NinjectDependencyResolver implement IDependencyResolver, which gives you the opportunity to resolve and inject any dependencies.
Using a Controller Activator
You can also introduce DI into controllers by creating a controller activator. You create this activator by implementing the IControllerActivator interface.
public class CustomControllerActivator : IControllerActivator { public IController Create(RequestContext requestContext, Type controllerType) { // Do anything you want here return (IController)DependencyResolver.Current.GetService(controllerType); } }
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new DefaultControllerFactory(new Infrastructure.CustomControllerActivator())); }
Overriding DefaultControllerFactory Methods
You can override methods in the DefaultControllerFactory class to customize the creation of controllers.
public class CustomDefaultControllerFactory: DefaultControllerFactory { protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { // Do anything you want return (IController)DependencyResolver.Current.GetService(controllerType); } }
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); ControllerBuilder.Current.SetControllerFactory(new Infrastructure.CustomDefaultControllerFactory()); }
Creating a Custom Action Invoker
If you derived your controller from the Controller class, then this is the responsibility of an action invoker. Action invokers are part of the functionality included in the Controller class. (If you create a controller directly from the IController interface, then you are responsible for executing the action yourself.)
An action invoker implements the IActionInvoker interface, the result type is a bool: a value of true indicates that the action was found and invoked and false indicates that the controller has no matching action.
public class CustomActionInvoker: IActionInvoker { public bool InvokeAction(ControllerContext controllerContext, string actionName) { // Code to execute action methods in controller if (actionName == "Index") { controllerContext.HttpContext.Response.Write("Response from Index action method"); return true; } else { return false; } } }
The action invoker associated with a controller is obtained through the Controller.ActionInvoker property. This means that different controllers in the same application can use different action invokers.
public class TestController : Controller { public TestController() { this.ActionInvoker = new CustomActionInvoker(); }
}
Using the Built-in Action Invoker
The built-in action invoker, which is the ControllerActionInvoker class, has some sophisticated techniques for matching requests to actions.
Using a Custom Action Name
Usually, the name of an action method determines the action that it represents. The Index action method services requests for the Index action. You can override this behavior using the ActionName attribute.
Using Action Method Selection
There are built-in attributes that work as selectors for the different kinds of HTTP requests: HttpPost for POST requests, HttpGet for GET requests, HttpPut for PUT requests, and so on. Another built-in attribute is NonAction, which indicates to the action invoker that a method that would otherwise be considered a valid action method should not be used.
Creating a Custom Action Method Selector
Action method selectors are derived from the ActionMethodSelectorAttribute class, it returns true from IsValidForRequest if the method is able to process a request, and false otherwise.
public class LocalAttribute : ActionMethodSelectorAttribute { public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo) { return controllerContext.HttpContext.Request.IsLocal; } }
Handling Unknown Actions
You can overriding the HandleUnknownAction method of Controller class.
Improving Performance with Specialized Controllers
Using Sessionless Controllers
Not all controllers need the session state features. In such cases, you can improve the performance of your application by avoiding work involved in maintaining session state. You do this by using sessionless controllers. These are just like regular controllers, with two exceptions: the MVC Framework will not load or store session state when they are used to process a request, and overlapping requests can be processed simultaneously.
[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)] public class AccountController : BaseController { public ActionResult Login(string returnUrl) { LoggerManager.DebugInfo.Info("Login Action"); ViewBag.ReturnUrl = returnUrl; return View(); }
}
When session state is Disabled, the HttpContext.Session property returns null.
Using Asynchronous Controllers
Asynchronous controllers are useful only for actions that are I/O- or network-bound and not CPU-intensive. The problem you are trying to solve with asynchronous controllers is a mismatch between the pool model and the type of request you are processing.
public async Task<ActionResult> GetData() { string data = await Task<string>.Factory.StartNew(() => { Thread.Sleep(5000); return "hello"; }); return View((object)data); }