Singleton每呼叫上下文(Web请求)在Unity
前几天我用ASP.Net线程有这个问题。 我想每个Web请求有一个单例对象。 我实际上需要这个工作单位。 我想为每个Web请求实例化一个工作单元,以便通过请求标识映射是有效的。 这样我就可以使用IoC将我自己的IUnitOfWork透明地注入到我的存储库类中,并且可以使用同一个实例来查询并更新我的实体。
由于我使用Unity,我错误地使用了PerThreadLifeTimeManager。 我很快意识到,ASP.Net线程模型不支持我想要的。 基本上它使用一个theadpool和回收线程,这意味着我得到一个UnitOfWork每个线程! 但是,我想要的是每个Web请求的一个工作单元。
有一点使用Google给了我这个伟大的职位 。 那正是我想要的; 除了很容易达到的统一部分。
这是我实现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<IUnitOfWork, MyDataContext>( new PerCallContextLifeTimeManager(), new InjectionConstructor());
希望能节省一些时间。
整洁的解决scheme,但LifetimeManager的每个实例应该使用一个唯一的键而不是一个常量:
private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid());
否则,如果你有多个注册PerCallContextLifeTimeManager的对象,它们共享相同的键来访问CallContext,并且你不会得到你想要的对象。
还值得实施RemoveValue,以确保对象被清理:
public override void RemoveValue() { CallContext.FreeNamedDataSlot(_key); }
虽然这很好的调用这个PerCallContextLifeTimeManager,我敢肯定这是不是 “安全”被视为一个ASP.Net每请求LifeTimeManager。
如果ASP.Net执行线程交换,那么通过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 🙁
关于此的更多信息可在以下url获得:
http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html
感谢您的贡献,
但是这个提议的实现有两个缺陷,其中一个是Steven Robbins在回答中已经提到的一个严重的错误,Micah Zoltu 在评论中提到了这个问题 。
- 调用上下文不保证由asp.net保存一个请求。 在负载下,它可以切换到另一个,导致build议的实现中断。
- 它不会在请求结束时处理释放依赖项。
目前,Unity.Mvc Nuget包提供了一个PerRequestLifetimeManager
来完成这个工作。 不要忘记在引导代码中注册相关的UnityPerRequestHttpModule
,否则依赖性释放也不会被处理。
使用引导
DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
或者在web.config system.webServer/modules
<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" />
看来它目前的实现也适合于网页表单。 而且它甚至不依赖于MVC。 不幸的是,它的程序集,因为它包含的其他类。
要小心,如果您使用已parsing的依赖关系使用某个自定义http模块,则可能已经将其放置在模块EndRequest
。 这取决于模块执行顺序。