Unity 中的单例每个调用上下文(Web 请求)
Posted
技术标签:
【中文标题】Unity 中的单例每个调用上下文(Web 请求)【英文标题】:Singleton Per Call Context (Web Request) in Unity 【发布时间】:2010-11-12 04:18:53 【问题描述】:几天前,我遇到了 ASP.Net 线程问题。我希望每个 Web 请求都有一个单例对象。我的工作单元实际上需要这个。我想为每个 Web 请求实例化一个工作单元,以便身份映射在整个请求中都是有效的。这样,我可以使用 IoC 将我自己的 IUnitOfWork 透明地注入到我的存储库类中,并且我可以使用相同的实例来查询然后更新我的实体。
由于我用的是Unity,所以误用了PerThreadLifeTimeManager。我很快意识到 ASP.Net 线程模型不支持我想要实现的目标。基本上它使用一个线程池并回收线程,这意味着我每个线程得到一个 UnitOfWork !但是,我想要的是每个 Web 请求一个工作单元。
有点谷歌搜索给了我this great post。这正是我想要的。除了很容易实现的统一部分。
这是我对 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());
希望它可以节省一些时间。
【问题讨论】:
不错的解决方案。如果可以的话,我建议将其重命名为“CallContextLifetimeManager”,因为 Web 请求可能只是潜在应用程序之一。 没错,我更新了文本和代码以反映这一点。谢谢。 使用 PerResolveLifetimeManager 有什么问题? 这不是问题!? 仅供参考,这不是问题的“正确”答案/解决方案。在 ASP.NET 中,单个请求可以(并且通常会在负载很重的情况下)在线程之间跳转。执行此操作时,不会保留 CallContext,只会迁移 HttpContext。如果您希望它在 ASP.NET 中可靠地工作(在负载下),您需要将 CallContext 更改为 HttpContext.Current.Items。 【参考方案1】:很好的解决方案,但是 LifetimeManager 的每个实例都应该使用唯一的键而不是常量:
private string _key = string.Format("PerCallContextLifeTimeManager_0", Guid.NewGuid());
否则,如果您在 PerCallContextLifeTimeManager 中注册了多个对象,它们将共享相同的密钥来访问 CallContext,您将无法取回预期的对象。
还值得实现 RemoveValue 以确保清理对象:
public override void RemoveValue()
CallContext.FreeNamedDataSlot(_key);
【讨论】:
-1 CallContext 在每个请求上重新创建。每个请求单例的不同实例之间的键不必是唯一的。 +1 事实上,它必须像六眼说的那样。如果您不分配唯一键,那么所有对象都在单个键下注册,事情就会变得一团糟。 -1 查看 Steven Robbins 的回答和 Micah Zoltu 对问题的评论:在负载下CallContext
不会为单个请求保留。【参考方案2】:
虽然调用这个 PerCallContextLifeTimeManager 很好,但我很确定这 不“安全”被视为 ASP.Net Per-request 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 :-(
更多信息请访问:
http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html
【讨论】:
【参考方案3】:感谢您的贡献,
但是提出的问题有两个缺点,其中之一是一个严重的错误,正如 Steven Robbins 在his answer 和 Micah Zoltu in a comment 中所述。
-
Asp.Net 不保证为单个请求保留调用上下文。在负载下,它可能会切换到另一个,从而导致建议的实现中断。
它不处理在请求结束时释放依赖项。
目前,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" />
它的当前实现似乎也适用于 Web 表单。它甚至不依赖于 MVC。不幸的是,它的程序集确实存在,因为它包含一些其他类。
请注意,如果您使用已解析的依赖项使用某些自定义 http 模块,它们可能已经在模块 EndRequest
中处理。这取决于模块执行顺序。
【讨论】:
以上是关于Unity 中的单例每个调用上下文(Web 请求)的主要内容,如果未能解决你的问题,请参考以下文章