ASP.NET MVC: HTTPContext и внедрение зависимостей - PullRequest
16 голосов
/ 18 мая 2009

В настоящее время у меня есть ActionFilter, который получает имя текущего пользователя из HttpContext и передает его в действие, которое использует его в методе службы. например:

Service.DoSomething(userName);

Теперь у меня есть причина сделать это не на уровне действий, а на уровне конструктора контроллера. В настоящее время я использую структурную карту для создания контроллеров и внедрения сервиса. Я смотрю на что-то вроде:

public interface IUserProvider
{
    string UserName { get; }
}

public class HttpContextUserProvider : IUserProvider
{
    private HttpContext context;

    public HttpContextUserProvider(HttpContext context)
    {
        this.context = context;
    }

    public string UserName
    {
        get
        {
            return context.User.Identity.Name;
        }
    }
}

Тем не менее, мой IoC foo действительно слабый, так как это первый проект, в котором я его использовал.

Так что мой вопрос ... как я могу сказать структуре структуры передать в HttpContext в конструкторе для HttpContextUserProvider? Это только кажется странным ... Я не уверен, как думать о HttpContext.

Ответы [ 4 ]

10 голосов
/ 08 февраля 2013

Похоже, вы должны использовать HttpContextBase вместо HttpContextUserProvider. Это готовая абстракция HttpContext, которая позволяет создавать макеты, писать UnitTests и внедрять ваши зависимости.

public class SomethingWithDependenciesOnContext
{
    public SomethingWithDependenciesOnContext(HttpContextBase context) {
        ...
    }

    public string UserName
    {
        get {return context.User.Identity.Name;}
    }
}

ObjectFactory.Initialize(x => 
          x.For<HttpContextBase>()
          .HybridHttpOrThreadLocalScoped()
          .Use(() => new HttpContextWrapper(HttpContext.Current));
8 голосов
/ 18 мая 2009

Есть интерфейс абстрактного HttpContext.Current. Выставляйте только те методы, которые вам нужны. Например, GetUserName() будет вызывать HttpContext.Current.User.Identity.Name в реализации. Сделайте это как можно тоньше.

Возьми эту абстракцию и добавь ее в класс другого провайдера. Это позволит вам протестировать провайдера, высмеивая абстракцию контекста http. В качестве дополнительного преимущества, вы можете делать другие изящные вещи с этой абстракцией HttpContext, кроме насмешек над ней. Повторно используйте это, с одной стороны. Добавьте универсальные параметры типа в пакеты и т. Д.

3 голосов
/ 18 мая 2009

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

2 голосов
/ 18 мая 2009

Возможно, я что-то пропустил, но приведенный выше ответ не работает для меня (с тех пор был удален - хотя он все еще был полезным - он показал, как сказать SM передавать аргументы конструктора). Вместо этого, если я сделаю:

ObjectFactory.Initialize(x =>
{
    x.BuildInstancesOf<HttpContext>()
         .TheDefault.Is.ConstructedBy(() => HttpContext.Current);
    x.ForRequestedType<IUserProvider>()
         .TheDefault.Is.OfConcreteType<HttpContextUserProvider>();
});

Я заставляю его работать. Я сделал это после нахождения: http://codebetter.com/blogs/jeremy.miller/archive/2008/03/20/if-you-need-something-in-structuremap-but-you-can-t-build-it-with-new.aspx


редактирование:

Благодаря ответу Брэда, я думаю У меня есть лучшая ручка для HttpContext. Его ответ определенно работает, я просто не уверен, что мне нравится иметь вызов HttpContext.Current внутри класса (кажется, что он скрывает зависимость, но я далеко не эксперт в этом деле).

Приведенный выше код должен работать для внедрения HttpContext, насколько я могу судить. Мэтт Хинце говорит о том, что, если все, что мне нужно от HttpContext, это User.Identity.Name, мой дизайн должен быть явным об этом (имея интерфейс вокруг HttpContext, показывающий только то, что мне нужно). Я думаю, что это хорошая идея.

Дело за обедом. Я вроде понял, что мой сервис должен зависеть только от строки: userName. Наличие зависимости от IUserProvider может не иметь большой добавленной стоимости. Так что я знаю, что не хочу, чтобы это зависело от HttpContext, и я знаю, что все, что мне нужно, это строка (userName) - мне нужно посмотреть, смогу ли я выучить достаточно StructureMap foo, чтобы установить это соединение для меня. (ответ sirrocoo дает подсказку о том, с чего начать, но он удалил его: * ().

...