ASP.NET MVC и Windsor.Castle: работа с HttpContext-зависимыми сервисами - PullRequest
12 голосов
/ 30 ноября 2009

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

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

Ответы [ 3 ]

24 голосов
/ 06 декабря 2009

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

5 голосов
/ 30 ноября 2009

С Castle Windsor вы можете использовать срок службы PerWebRequest - который должен вполне соответствовать вашим требованиям.

Это означает, что вы можете просто внедрить содержимое HTTP в ваши службы, и контейнер позаботится о правильном управлении временем жизни. Однако для этого требуется, чтобы вы также зарегистрировали все эти сервисы (и всех потребителей этих сервисов и т. Д.) Как PerWebRequest (или Transient), потому что, если вы зарегистрируете их как Singletons, они будут удерживать устаревшие (и, возможно, удаленные) контексты.

4 голосов
/ 29 июня 2011

Я только что столкнулся с этой же проблемой, но мое решение несколько иное.

Интерфейс:

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

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

...