ASP.NET MVC: HTTPContext и зависимая инъекция
В настоящее время у меня есть 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.
Ответы
Ответ 1
Имейте абстрактный интерфейс HttpContext.Current. Выставлять только те методы, которые вам нужны. GetUserName()
будет вызывать HttpContext.Current.User.Identity.Name в реализации, например. Сделайте это настолько тонким насколько возможно.
Возьмите эту абстракцию и введите ее в свой другой класс провайдера. Это позволит вам протестировать поставщика, высмеивая абстракцию контекста HTTP. В качестве побочного преимущества вы можете делать другие отличные вещи с помощью абстракции HttpContext, помимо этого. Повторно используйте его, с одной стороны. Добавьте общие параметры типа в пакеты и т.д.
Ответ 2
Похоже, вы должны использовать 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));
Ответ 3
Я не уверен, почему ты беспокоишься. Кажется, что просто использовать HttpContext.Current прямо в HttpContextUserProvider - это правильная вещь. Вы никогда не будете заменять другой HttpContext...
Ответ 4
Возможно, я что-то оставил, но приведенный выше ответ не работает для меня (с тех пор он был удален - это был еще полезный ответ, хотя - он показал, как сообщить 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, и я знаю, что все, что мне нужно, это строка (имя_пользователя). Мне нужно узнать, могу ли я достаточно изучить StructureMap foo, чтобы установить это соединение для меня. (ответ sirrocoo дает подсказку о том, с чего начать, но он удалил его: *().