Как настроить IoC, когда ключевому классу нужен сеанс (или другая переменная контекста)

Я пытаюсь выяснить, как использовать IoC в ситуациях, когда зависимые классы могут меняться на основе некоторой переменной в приложении (в данном случае состояния сеанса). Например, у каждого нашего клиента есть другая база данных, поэтому соединение с базой данных должно быть построено на значении, хранящемся на их сеансе (в частности, поскольку некоторые пользователи могут иметь несколько баз данных, если они владеют несколькими предприятиями и будут переключаться между базами данных),

Вот общий пример того, как мы сейчас настроили эту структуру:

public class MyTestController : ControllerBase
{
    Repository _rep;

    public MyTest(Repository rep)
    {
        _rep = rep;
    }

    public MyTest()
    {
        string connString = String.Format("Server={0}; Database={1};"
            , SessionContainer.ServerName, SessionContainer.DatabaseName;
        var dc = new DataContext(connString);
        _rep = new Repository(dc);
    }

    public int SampleFn()
    {
        return _rep.GetCountOfEmployees();
    }
}

public class Repository
{
    DataContext _context;

    public Repository(DataContext context)
    {
        _context = context;
    }
} 

Можем ли мы установить это с помощью IoC и устранить стандартные c-tors? Если да, то как? У меня нет проблемы, просто используя D.I. как это, но я хотел бы изучить возможность StructureMap или Unity (обратите внимание: мы обычно проходим в db/server в класс factory, который строит datacontext... выше пример просто для краткости).

Ответы

Ответ 1

Как создается экземпляр репозитория, а также его время жизни, это не касается контроллера.

Когда вы регистрируете компоненты в контейнере, вы должны указать время жизни компонента. В зависимости от вашей реализации вы можете просто выбрать, чтобы жизненный цикл репозитория выполнялся после сеанса.

В любом случае вы можете использовать factory для создания репозитория из сеанса, но делать это извне контроллера.

Вам определенно нужно избавиться от конструктора по умолчанию.


Сверху моей головы я не могу вспомнить, как это сделать в Unity или StructureMap, поэтому здесь приведен пример Castle Windsor.

Определить абстрактный Factory:

public interface IRepositoryFactory
{
    Repository Create();
}

и реализация

public class MyRepositoryFactory : IRepositoryFactory
{
    private readonly HttpContextBase httpContext;

    public MyRepositoryFactory(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        this.httpContext = httpContext;
    }

    #region IRepositoryFactory Members

    public Repository Create()
    {
        // return Repository created from this.httpContext
    }

    #endregion
}

Теперь зарегистрируйте все материалы

container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IRepositoryFactory>()
    .ImplementedBy<MyRepositoryFactory>()
    .LifeStyle.PerWebRequest);
container.Register(Component.For<Repository>()
    .UsingFactory((IRepositoryFactory f) => f.Create())
    .LifeStyle.PerWebRequest);

Здесь я использовал стиль PerWebRequest, но если вы хотите оптимизировать, вы можете создать собственный стиль PerWebSession. Это не так сложно сделать в замке, но я не могу вспомнить, как сильно это происходит в других контейнерах DI.

Вам также нужно будет зарегистрировать HttpContextBase, поскольку MyRepositoryFactory зависит от него.