Подтвердить что ты не робот

ASP.NET MVC & Windsor.Castle: работа с службами, зависящими от HttpContext

У меня есть несколько служб инъекций зависимостей, которые зависят от таких вещей, как HTTP-контекст. Сейчас я настраиваю их как singleletons контейнер Windsor в обработчике Application_Start, что, очевидно, является проблемой для таких служб.

Каков наилучший способ справиться с этим? Я планирую сделать их переходными, а затем освободить их после каждого HTTP-запроса. Но каков наилучший способ/место для вставки контекста HTTP в них? Контроллер factory или где-то еще?

4b9b3361

Ответ 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, и легко высмеять это при необходимости.

Я действительно слишком усложняю вещи или есть какие-то предостережения в моем решении?