45

几天前,我遇到了 ASP.Net 线程问题。我希望每个 Web 请求都有一个单例对象。我的工作单元实际上需要这个。我想为每个 Web 请求实例化一个工作单元,以便身份映射在整个请求中都是有效的。这样,我可以使用 IoC 将我自己的 IUnitOfWork 透明地注入到我的存储库类中,并且我可以使用相同的实例来查询然后更新我的实体。

由于我使用的是 Unity,所以我错误地使用了 PerThreadLifeTimeManager。我很快意识到 ASP.Net 线程模型不支持我想要实现的目标。基本上它使用一个线程池并回收线程,这意味着我每个线程得到一个 UnitOfWork !但是,我想要的是每个 Web 请求一个工作单元。

一点谷歌搜索给了我这个很棒的帖子。这正是我想要的。除了很容易实现的统一部分。

这是我对 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());

希望它可以节省一些时间。

4

3 回答 3

25

整洁的解决方案,但 LifetimeManager 的每个实例都应该使用唯一的键而不是常量:

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

否则,如果您在 PerCallContextLifeTimeManager 中注册了多个对象,它们将共享相同的密钥来访问 CallContext,并且您将无法取回预期的对象。

同样值得实现 RemoveValue 以确保清理对象:

public override void RemoveValue()
{
     CallContext.FreeNamedDataSlot(_key);
}
于 2010-06-10T11:20:50.807 回答
21

虽然调用这个 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

于 2011-01-15T05:11:54.533 回答
4

感谢您的贡献,

但是提出的实施问题有两个缺点,其中一个是一个严重的错误,正如 Steven Robbins 在他的回答和 Micah Zoltu在评论中已经指出的那样。

  1. Asp.Net 不保证为单个请求保留调用上下文。在负载下,它可以切换到另一个,导致建议的实现中断。
  2. 它不处理在请求结束时释放依赖项。

目前,Unity.Mvc Nuget 包提供了一个PerRequestLifetimeManager用于完成这项工作的工具。不要忘记UnityPerRequestHttpModule在引导代码中注册其关联,否则也不会处理依赖关系释放。

使用自举

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

或在 web.configsystem.webServer/modules

<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" />

它的当前实现似乎也适用于 Web 表单。它甚至不依赖于 MVC。不幸的是,它的程序集确实如此,因为它包含一些其他类。

请注意,如果您使用已解析的依赖项使用某些自定义 http 模块,它们可能已经在模块中处理EndRequest。这取决于模块执行顺序。

于 2015-04-03T09:38:10.053 回答