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(/* control开发者_运维问答ler factory with global unity container */);
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.
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.
精彩评论