ControllerFactory for specific portable area
S

2

7

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 */);
Slavery answered 17/3, 2011 at 21:37 Comment(0)
T
12

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.

Tamas answered 20/3, 2011 at 6:22 Comment(0)
A
-1

I don't think you can have different factory per area, but you can in your single factory, put the controller into different container, based on the area. I think but not sure, that you can find your area in CreateController method of your factory by querying this:

requestContext.RouteData.DataTokens["area"];

where requestContext is passed to CreateController as a parameter.

Ackley answered 18/3, 2011 at 1:54 Comment(1)
@smartcaveman: thank you for leaving this comment - by doing this you allowed me 1) learning something new 2) upvoting you =)Ackley

© 2022 - 2024 — McMap. All rights reserved.