ControllerFactory для специфической портативной области


Мой главный ASP.NET MVC composite application использует глобальный контейнер Unity для регистрации типов. Приложение настраивает фабрику контроллеров на использование этого глобального контейнера. Я хотел бы переработать это так, чтобы каждая из моих переносимых областей использовала свой собственный дочерний контейнер Unity, так что различные области могут реализовывать интерфейсы различными способами. Похоже, мне нужно было бы иметь другой ControllerFactory для каждой области. Как бы я этого добился, учитывая, что следующее устанавливает завод для все?

ControllerBuilder.Current
    .SetControllerFactory(/* controller factory with global unity container */);
2 6

2 ответа:

Можно использовать MasterControllerFactory, который содержит все реализации IControllerFactory для каждой области и знает, какая фабрика может построить какую RequestContext. Это фактически позволяет выбрать другой ControllerFactory для любого изменения, а не только по площади. Вот как это работает:

Все реализации фабрики контроллера области должны реализовывать IFilteredControllerFactory вместо IControllerFactory. Вот оно:

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

Пример реализации фильтра на основе имени области выглядит так: это:

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...
    }

}

Тогда вам нужен MasterControllerFactory, который выглядит следующим образом:

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);
    }
}

В Application_Start Вашего global.asax вам все еще нужно настроить все, но это легко.

var masterControllerFactory = new MasterControllerFactory();
masterControllerFactory.Register(new Area51ControllerFactory());
ControllerBuilder.Current.SetControllerFactory(masterControllerFactory);
Очевидно, что вы можете настроить это несколькими способами, чтобы лучше работать со своим стилем кодирования и архитектурой приложения.

Я не думаю, что у вас может быть разная фабрика для каждой области, но вы можете в своей отдельной фабрике поместить контроллер в другой контейнер, основанный на области. Я Думаю, но не уверен, что вы можете найти свою область в методе CreateController вашей фабрики, запросив это:

requestContext.RouteData.DataTokens["area"];

, где requestContext передается в CreateController в качестве параметра.