Code Voyeur
RSS
Data Access Languages MVC ORM About Roadmap Contact Site Map RSS Sample Code Presentations Snippets dll Hell .net Rate My Snippet

A Simple IronPython ControllerFactory for ASP.NET MVC

The default behavior for ASP.NET MVC is to wire up a controller instance to the controller value found in a request's RouteData. This mapping is desirable for its simplicity, but limited in its ability to provide an extensible configuration model. Managing properties on controllers would likely require either a custom configuration section or extensive use of AppSettings.

An alternative approach would be to use an IoC container, such as Spring.NET. This approach simply manages controller instances as standard Spring container objects. An MVC contrib project already provides support for a Spring Controller factory (among others). This article will present an alternative approach in which controller instances are configured in an IronPython script that lives alongside the MVC application.

public class CustomControllerFactory : IControllerFactory {
    #region IControllerFactory Members

    public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) {
        ...
    }

    public void ReleaseController(IController controller) {
        ...
    }

    #endregion
}
Implementing the IControllerFactory instance is somewhat trivial. Only CreateController needs be implemented to allow for contoller instantiation.
private static Dictionary<string, object> _objects = new Dictionary<string, object>();
private static ScriptRuntimeSetup _setup = null;
private static ScriptRuntime _runtime = null;
The PyControllerFactory will make use of three private static objects to manage controller creation. The _controllers dictionary is used to store the controller configuration functions (see below) and a static instance of the DLR's ScriptRuntime is maintained to handle all incoming requests.
static PyControllerFactory() {

    _setup = ScriptRuntimeSetup.ReadConfiguration();
    _runtime = new ScriptRuntime(_setup);

    _runtime.LoadAssembly(Assembly.GetExecutingAssembly());
    
    string path = HttpContext.Current.Server.MapPath("~/App_Data/Controllers.py");
     _runtime.GetEngine("IronPython").ExecuteFile(path, _runtime.Globals);
}
The real work is done in a static constructor that will execute the Python script. The executing assembly is passed into the runtime, with the assumption that the controllers will be found in this assembly. A more complete example would clearly allow for a list of assembly references to be included. Next, the Python file is executed.
from IronPythonMVCControllerFactory.Controllers import *

def home():
    hc = HomeController()
    hc.ViewData["WelcomeMessage"] = "Welcome to Code Voyeur"
    return hc
    
def profile():        
    pc = ProfileController()
    pc.ViewData["WelcomeMessage"] = "Update your profile."
    return pc
    
def news():
    nc = NewsController()
    nc.ViewData["WelcomeMessage"] = "News about Code Voyeur"
    return nc
Unlike previous CodeVoyeur articles that use IronPython as an external configuration language, this sample does not use XML to organize data. Instead, each controller name is mapped to a function name. The return value of each function is a configured instance of a controller.
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) {

    if (! _controllers.ContainsKey(controllerName)) {
        
        _controllers[controllerName] = _runtime.Globals.GetVariable(controllerName.ToLower());                
    }

    return _runtime.Operations.Call(_controllers[controllerName]) as IController;
             
}
The CreateController implementation is then very straight forward. This factory method takes advantage of the fact that a function is just another piece of data in a Python script. First, the _controllers Dictionary is checked to see if an object's prototype function has already been called (it would be stored if so). If not, the _runtime's Globals collection is interrogated. The name of the controller should match the name of a function. The function reference is then stored in the _controllers dictionary. Next, the Call method of _runtime's Operations property is called. This call simply invokes the Python function, returning a fully constructed Controller instance.
protected void Application_Start() {
    RegisterRoutes(RouteTable.Routes);

    ControllerBuilder.Current.SetControllerFactory(new PyControllerFactory());
}
The PyControllerFactory is wired to the MVC applicaiton in the Global ASAX.

While this solution clearly is not as powerful as a full fledged IoC container, it achieves much of the same functionality in relatively few lines of code and without XML.

Download Sample Project

Article Posted: Thursday, January 15, 2009

Comments

Posted by Cheyanna on 7/4/2011 3:44:50 PM
There?s a sercet about your post. ICTYBTIHTKY

Leave a Comment

Shout it

Contact Code Voyeur about this article.