ASP.NET MVC & Windsor.Castle: работа с службами, зависящими от HttpContext
У меня есть несколько служб инъекций зависимостей, которые зависят от таких вещей, как HTTP-контекст. Сейчас я настраиваю их как singleletons контейнер Windsor в обработчике Application_Start, что, очевидно, является проблемой для таких служб.
Каков наилучший способ справиться с этим? Я планирую сделать их переходными, а затем освободить их после каждого HTTP-запроса. Но каков наилучший способ/место для вставки контекста HTTP в них? Контроллер factory или где-то еще?
Ответы
Ответ 1
С Castle Windsor вы можете использовать время жизни PerWebRequest
, которое должно соответствовать вашим требованиям.
Это означает, что вы можете просто вводить материал HTTP в свои сервисы, и контейнер будет заботиться о правильном управлении жизненным циклом. Однако для этого требуется также зарегистрировать все эти сервисы (и все пользователи этих служб и т.д.) Как PerWebRequest (или Transient), потому что, если вы зарегистрируете их как Singletons, они будут придерживаться устаревших (и, возможно, удаленных) контекстов.
Ответ 2
Как и Марк, вам нужно зарегистрировать эти http-зависимые сервисы либо как PerWebRequest или Transient. Здесь образец, который показывает, как регистрировать и вводить HttpRequest или HttpContext:
public class Service {
private readonly HttpRequestBase request;
public Service(HttpRequestBase request) {
this.request = request;
}
public string RawUrl {
get {
return request.RawUrl;
}
}
}
...
protected void Application_Start(object sender, EventArgs e) {
IWindsorContainer container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.AddComponentLifeStyle<Service>(LifestyleType.Transient);
container.Register(Component.For<HttpRequestBase>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => new HttpRequestWrapper(HttpContext.Current.Request)));
container.Register(Component.For<HttpContextBase>()
.LifeStyle.PerWebRequest
.UsingFactoryMethod(() => new HttpContextWrapper(HttpContext.Current)));
}
Используя HttpRequestBase
вместо HttpRequest
, вы можете легко высмеять его для тестирования.
Кроме того, не забудьте зарегистрировать PerWebRequestLifestyleModule
в вашем web.config.
Ответ 3
Я столкнулся с этой же проблемой, но мое решение несколько другое.
Интерфейс:
public interface IHttpContextProvider
{
/// <summary>
/// Gets the current HTTP context.
/// </summary>
/// <value>The current HTTP context.</value>
HttpContextBase Current { get; }
}
Реализация:
/// <summary>
/// A default HTTP context provider, returning a <see cref="HttpContextWrapper"/> from <see cref="HttpContext.Current"/>.
/// </summary>
public class DefaultHttpContextProvider : IHttpContextProvider
{
public HttpContextBase Current
{
get { return new HttpContextWrapper(HttpContext.Current); }
}
}
Затем я регистрирую IHttpContextProvider
как одноэлементный контейнер.
Я все еще немного новичок, когда речь заходит о DI, поэтому, возможно, я слишком усложняю ситуацию, но из того, что я могу понять, я не могу зависеть от компонентов одного элемента от компонентов PerWebRequest, что имеет смысл (но что все примеры делают). В моем решении я зависим от HttpContext.Current
в изолированном компоненте, и я не заинтересован в его тестировании. Но каждый компонент, который нуждается в доступе к HTTP-контексту, может получить это, в зависимости от IHttpContextProvider
, и легко высмеять это при необходимости.
Я действительно слишком усложняю вещи или есть какие-то предостережения в моем решении?