Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ControllerFactory for specific portable area

My main ASP.NET MVC composite application uses a global Unity container to register types. The app sets the controller factory up to use this global container. I would like to refactor this such that each one of my portable areas leverages it's own child Unity container, so that different areas can implement interfaces in varying ways. It seems like I would need to have a different ControllerFactory per area. How would I accomplish that, given that the following sets the factory for all?

ControllerBuilder.Current
    .SetControllerFactory(/* controller factory with global unity container */);
like image 497
dreadwail Avatar asked Mar 17 '11 21:03

dreadwail


1 Answers

You can use a MasterControllerFactory that contains all the IControllerFactory implementations for each area, and knows which factory can build which RequestContext. This actually allows you to select a different ControllerFactory for any variation, not just by area. Here's how it works:

All of the area controller factory implementations must implement IFilteredControllerFactory instead of IControllerFactory. Here it is:

public interface IFilteredControllerFactory:IControllerFactory
{
    bool CanHandle(RequestContext requestContext);
}  

An example of an implementation that filters based on the area name looks like this:

public class Area51ControllerFactory:IFilteredControllerFactory
{
    public bool CanHandle(RequestContext requestContext)
    {
        return requestContext.RouteData.DataTokens["area"].ToString().ToLowerInvariant() == "area51";
    }
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
        // create a controller...
    }

    public void ReleaseController(IController controller)
    {
        // release the controller...
    }

}

Then you need the MasterControllerFactory, which looks like this:

public class MasterControllerFactory : DefaultControllerFactory
{
    private readonly List<IFilteredControllerFactory> _slaveFactories;
    public MasterControllerFactory()
    {
        _slaveFactories = new List<IFilteredControllerFactory>();
    }
    public void RegisterFactory(IFilteredControllerFactory slaveFactory)
    {
        if(slaveFactory!=null && !_slaveFactories.Contains(slaveFactory))
        {
            _slaveFactories.Add(slaveFactory);
        }
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var factory = _slaveFactories.FirstOrDefault(x => x.CanHandle(requestContext));
        if(factory!=null)
        {
            return factory.CreateController(requestContext, controllerName);
        }
        return base.CreateController(requestContext, controllerName);
    }

    public override void ReleaseController(IController controller)
    {
        bool released = false;
        if (controller is Controller)
        {
            var requestContext = ((Controller) controller).ControllerContext.RequestContext;
            var factory = _slaveFactories.FirstOrDefault(x => x.CanHandle(requestContext));
            if (factory != null)
            {
                factory.ReleaseController(controller);
                released = true;
            }
        }
        if(!released)base.ReleaseController(controller);
    }
}

In the Application_Start of your global.asax you still need to set everything up, but that's easy.

var masterControllerFactory = new MasterControllerFactory();
masterControllerFactory.Register(new Area51ControllerFactory());
ControllerBuilder.Current.SetControllerFactory(masterControllerFactory);

Obviously, you can tweak this a number of ways to work best with your coding style and application architecture.

like image 191
smartcaveman Avatar answered Sep 30 '22 00:09

smartcaveman