Как настроить 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.