Контекст Singleton Per Call (веб-запрос) в Unity

Несколько дней назад у меня была эта проблема с streamами ASP.Net. Я хотел иметь одноэлементный объект для каждого веб-запроса. Мне действительно нужно это для моей единицы работы. Я хотел бы создать экземпляр единицы работы для каждого веб-запроса, чтобы карта идентификации была действительной в рамках запроса. Таким образом, я мог бы использовать IoC, чтобы прозрачно вводить свои собственные classы IUnitOfWork в свои classы репозитория, и я мог бы использовать один и тот же экземпляр для запроса, а затем для обновления моих объектов.

Поскольку я использую Unity, я ошибочно использовал PerThreadLifeTimeManager. Вскоре я понял, что модель streamовой передачи ASP.Net не поддерживает то, что я хочу добиться. В основном это использует theadpool и перерабатывает streamи, и это означает, что я получаю один UnitOfWork в stream! Однако то, что я хотел, было одной единицей работы для каждого веб-запроса.

Немного поискового робота дал мне этот отличный пост . Это было именно то, что я хотел; за исключением части единства, которая была довольно легко достижимой.

Это моя реализация для PerCallContextLifeTimeManager для единства:

public class PerCallContextLifeTimeManager : LifetimeManager { private const string Key = "SingletonPerCallContext"; public override object GetValue() { return CallContext.GetData(Key); } public override void SetValue(object newValue) { CallContext.SetData(Key, newValue); } public override void RemoveValue() { } } 

И, конечно, я использую это, чтобы зарегистрировать свою часть работы с кодом, подобным этому:

 unityContainer .RegisterType( new PerCallContextLifeTimeManager(), new InjectionConstructor()); 

Надеюсь, это сэкономит кому-то немного времени.

Аккуратное решение, но каждый экземпляр LifetimeManager должен использовать уникальный ключ, а не константу:

 private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid()); 

В противном случае, если у вас есть несколько объектов, зарегистрированных в PerCallContextLifeTimeManager, они используют один и тот же ключ для доступа к CallContext, и вы не вернете ожидаемый объект.

Также стоит выполнить RemoveValue для обеспечения очистки объектов:

 public override void RemoveValue() { CallContext.FreeNamedDataSlot(_key); } 

Хотя это прекрасно называется PerCallContextLifeTimeManager, я уверен, что это не «безопасно», чтобы считаться ASP.Net Per-request LifeTimeManager.

Если ASP.Net выполняет свопинг, тогда единственное, что переносится в новый stream через CallContext, это текущий HttpContext – все, что вы храните в CallContext, исчезнет. Это означает, что при большой нагрузке вышеприведенный код может иметь непреднамеренные результаты – и я полагаю, что было бы настоящей болью отследить, почему!

Единственный «безопасный» способ сделать это – с помощью HttpContext.Current.Items или сделать что-то вроде:

 public class PerCallContextOrRequestLifeTimeManager : LifetimeManager { private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid()); public override object GetValue() { if(HttpContext.Current != null) return GetFromHttpContext(); else return GetFromCallContext(); } public override void SetValue(object newValue) { if(HttpContext.Current != null) return SetInHttpContext(); else return SetInCallContext(); } public override void RemoveValue() { } } 

Это, очевидно, означает принятие зависимостей от System.Web 🙁

Гораздо больше информации об этом можно получить по адресу:

http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html

Спасибо за ваш вклад,

Но предлагаемый вариант вопроса имеет два недостатка, один из которых является серьезной ошибкой, как уже было сказано Стивеном Роббинсом в его ответе, и Мика Золту в комментарии .

  1. Контекст вызова не гарантируется сохранением asp.net для одного запроса. Под нагрузкой он может переключиться на другой, что приведет к разрыву предлагаемой реализации.
  2. Он не обрабатывает освобождение зависимостей в конце запроса.

В настоящее время пакет Unity.Mvc Nuget предоставляет PerRequestLifetimeManager для выполнения работы. Не забудьте зарегистрировать связанный с ним UnityPerRequestHttpModule в коде начальной загрузки, иначе освобождение зависимостей также не будет выполнено.

Использование начальной загрузки

 DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

или в web.config system.webServer/modules

  

Похоже, его текущая реализация также подходит для веб-форм. И это даже не зависит от MVC. К сожалению, его assembly не является причиной некоторых других classов, которые она содержит.

Остерегайтесь, если вы используете какой-либо пользовательский http-модуль, используя ваши разрешенные зависимости, они могут быть уже размещены в модуле EndRequest . Это зависит от порядка выполнения модуля.

Давайте будем гением компьютера.