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

Я пытаюсь выяснить, как использовать 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? Если так, то как? У меня нет проблем, просто используя DI, как это, но я хотел бы изучить возможность StructureMap или Unity (примечание: мы обычно передаем db / server классу фабрики, который строит текст данных... приведенный выше пример это просто для краткости).

1 ответ

Решение

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

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

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

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


Я не могу вспомнить, как это сделать в Unity или StructureMap, так что вот пример Castle Windsor.

Определите абстрактную фабрику:

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.

Другие вопросы по тегам