StructureMap IOC/DI и создание объектов
Я строю небольшой интернет-магазин с asp.net mvc и Structuremap ioc/di. Мой класс Basket использует объект сессии для сохранения, и я хочу использовать SM для создания моего объекта корзины через интерфейс IBasket. Моя реализация корзины требует HttpSessionStateBase (оболочка состояния сеанса из mvc) в конструкторе, который доступен внутри Controller/Action. Как зарегистрировать мою реализацию IBasket для SM?
Это интерфейс моей корзины:
public interface IBasketService {
BasketContent GetBasket();
void AddItem(Product productItem);
void RemoveItem(Guid guid);
}
И СМ регистрация:
ForRequestedType(typeof (IBasketService)).TheDefaultIsConcreteType(typeof (StoreBasketService));
Но моя реализация StoreBasketService имеет конструктор:
public StoreBasketService(HttpSessionStateBase sessionState)
Как мне предоставить объект HttpSessionStateBase для SM, который доступен только в контроллере?
Это мое первое использование SM IOC/DI, и я не могу найти решение / пример в официальной документации и на веб-сайте;)
5 ответов
Если вам абсолютно необходимо, чтобы ваш StoreBasketService использовал сеанс, я бы соблазнился определить интерфейс и оболочку вокруг HttpSessionState вместо использования HttpSessionStateBase, чтобы вы могли также зарегистрировать его в StructureMap. Оболочка получала бы состояние сеанса из текущего контекст. Зарегистрируйте обертку в StructureMap и затем пусть ваш StoreBasketService принимает интерфейс в качестве аргумента конструктора. Структурная карта должна затем знать, как создать экземпляр интерфейсной оболочки и внедрить его в ваш класс StoreBasketService.
Использование интерфейса и оболочки позволит вам смоделировать оболочку в ваших модульных тестах, так же, как HttpSessionStateBase позволяет имитировать фактическую сессию.
public interface IHttpSessionStateWrapper
{
HttpSessionState GetSessionState();
}
public class HttpSessionStateWrapper : IHttpSessionStateWrapper
{
public virtual HttpSessionState GetSessionState()
{
return HttpContext.Current.Session;
}
}
ForRquestedType(typeof(IHttpSessionStateWrapper))
.TheDefaultIsConcreteType(typeof(IHttpSessionStateWrapper));
public class StoreBasketService
{
HttpSessionState session;
public StoreBasketService( IHttpSessionstateWrapper wrapper )
{
session = wrapper.GetSessionState();
}
// basket implementation ...
}
Тем не менее, вы можете иметь StructureMap фактически хранить вашу корзину в сеансе, используя .CacheBy(InstanceScope.HttpContext)
при регистрации. На самом деле может быть лучше, если ваш StoreBasketService реализует внутреннее хранилище, а не хранит вещи в сеансе - тогда вы полностью потеряете зависимость от состояния сеанса (с точки зрения вашего класса), и ваше решение может быть проще. Ваше внутреннее хранилище может быть Dictionary<Guid,Product>
так как вы получаете доступ к ним через ваш интерфейс.
Смотрите также:
http://www.lostechies.com/blogs/chad_myers/archive/2008/07/15/structuremap-basic-scenario-usage.aspx
ForRequestedType<IBasketService>()
.TheDefault.Is.OfConcreteType<StoreBasketService>()
.WithCtorArg("sessionState").EqualTo(HttpContext.Current.Session);
?? это работает?
Я только что сделал свою первую попытку создания настраиваемой области видимости... создаю небольшое веб-приложение с ее помощью, и, насколько я вижу, это похоже на работу. Это кеширует объект внутри текущего сеанса пользователя и возвращает тот же объект, пока вы остаетесь в том же сеансе:
public class HttpSessionBuilder : CacheInterceptor
{
private readonly string _prefix = Guid.NewGuid().ToString();
protected override CacheInterceptor clone()
{
return this;
}
private string getKey(string instanceKey, Type pluginType)
{
return string.Format("{0}:{1}:{2}", pluginType.AssemblyQualifiedName, instanceKey, this._prefix);
}
public static bool HasContext()
{
return (HttpContext.Current.Session != null);
}
protected override bool isCached(string instanceKey, Type pluginType)
{
return HttpContext.Current.Session[this.getKey(instanceKey, pluginType)] != null;
}
protected override object retrieveFromCache(string instanceKey, Type pluginType)
{
return HttpContext.Current.Session[this.getKey(instanceKey, pluginType)];
}
protected override void storeInCache(string instanceKey, Type pluginType, object instance)
{
HttpContext.Current.Session.Add(this.getKey(instanceKey, pluginType), instance);
}
}
Вы должны настроить ObjectFactory следующим образом в global.asax Application_start
ObjectFactory.Initialize(x=>
x.ForRequestedType<MyClass>().InterceptConstructionWith(new HttpSessionBuilder()));
Вы также можете использовать один из методов ObjectFactory.Inject для внедрения HttpSessionStateBase в StructureMap. Затем он вызвал бы конструктор с введенным HttpSessionStateBase.
Я только начал с StructureMap, и я не получаю результаты, которые вы описываете. Я выполнил простой тест с использованием простого класса, настроив Structuremap для кэширования с помощью HttpContext, и, как я вижу, CacheBy.HttpContext означает, что в рамках одного и того же запроса вы получите один и тот же экземпляр... не в рамках одного сеанса.
Конструктор моего класса устанавливает дату / время в приватном поле. У меня есть кнопка, которая получает 2 экземпляра MyClass с интервалом в одну секунду... Затем в метке отображается время обоих экземпляров.
При первом нажатии этой кнопки объекты A и B являются одним и тем же экземпляром, поскольку время их создания точно такое же, как и ожидалось.
Нажав кнопку еще раз, можно ожидать, что время создания не изменилось, если экземпляры будут кэшироваться в сеансе... однако в моем тесте я получаю новое время создания...
Конфигурация структуры карты:
ObjectFactory.Initialize(x=>x.ForRequestedType<MyClass>(). CacheBy(InstanceScope.HttpContext));
Событие нажатой кнопки на тестовой странице
protected void btnTest_Click(object sender, EventArgs e)
{
MyClass c = ObjectFactory.GetInstance<MyClass>();
System.Threading.Thread.Sleep(1000);
MyClass b = ObjectFactory.GetInstance<MyClass>();
lblResult.Text = String.Format("cache by httpcontext First:{0} Second:{1} session id {2} ", c.GetTimeCreated(), b.GetTimeCreated(),Session.SessionID);
}
Мои занятия
public class MyClass
{
private DateTime _timeCreated;
public MyClass()
{
_timeCreated = DateTime.Now;
}
public string GetTimeCreated()
{
return _timeCreated.ToString("dd/MM/yyyy hh:mm:ss");
}
}