Контекст Singleton Per Call (веб-запрос) в Unity
Несколько дней назад у меня была эта проблема с streamами ASP.Net. Я хотел иметь одноэлементный объект для каждого веб-запроса. Мне действительно нужно это для моей единицы работы. Я хотел бы создать экземпляр единицы работы для каждого веб-запроса, чтобы карта идентификации была действительной в рамках запроса. Таким образом, я мог бы использовать IoC, чтобы прозрачно вводить свои собственные classы IUnitOfWork в свои classы репозитория, и я мог бы использовать один и тот же экземпляр для запроса, а затем для обновления моих объектов.
Поскольку я использую Unity, я ошибочно использовал PerThreadLifeTimeManager. Вскоре я понял, что модель streamовой передачи ASP.Net не поддерживает то, что я хочу добиться. В основном это использует theadpool и перерабатывает streamи, и это означает, что я получаю один UnitOfWork в stream! Однако то, что я хотел, было одной единицей работы для каждого веб-запроса.
Немного поискового робота дал мне этот отличный пост . Это было именно то, что я хотел; за исключением части единства, которая была довольно легко достижимой.
- Шаблон страtagsи и зависимость от использования с помощью Unity
- Единство взыскания - условное разрешение
- Использование IsAssignableFrom и ключевое слово «is» в C #
- Лучше ли создать одноэлементный доступ к контейнеру единства или передать его через приложение?
- Разрешение classов без регистрации их с помощью Castle Windsor
Это моя реализация для 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
Спасибо за ваш вклад,
Но предлагаемый вариант вопроса имеет два недостатка, один из которых является серьезной ошибкой, как уже было сказано Стивеном Роббинсом в его ответе, и Мика Золту в комментарии .
- Контекст вызова не гарантируется сохранением asp.net для одного запроса. Под нагрузкой он может переключиться на другой, что приведет к разрыву предлагаемой реализации.
- Он не обрабатывает освобождение зависимостей в конце запроса.
В настоящее время пакет Unity.Mvc Nuget предоставляет PerRequestLifetimeManager
для выполнения работы. Не забудьте зарегистрировать связанный с ним UnityPerRequestHttpModule
в коде начальной загрузки, иначе освобождение зависимостей также не будет выполнено.
Использование начальной загрузки
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
или в web.config system.webServer/modules
Похоже, его текущая реализация также подходит для веб-форм. И это даже не зависит от MVC. К сожалению, его assembly не является причиной некоторых других classов, которые она содержит.
Остерегайтесь, если вы используете какой-либо пользовательский http-модуль, используя ваши разрешенные зависимости, они могут быть уже размещены в модуле EndRequest
. Это зависит от порядка выполнения модуля.