Unity 中的单例每次调用上下文(Web 请求)

发布于 2024-07-26 23:19:38 字数 1065 浏览 13 评论 0原文

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

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

一些谷歌搜索给了我

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

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

A few days ago, I had an issue with ASP.Net threading. I wanted to have a singleton object per web request. I actually need this for my unit of work. I wanted to instantiate a unit of work per web request so that identity map is valid through out the request. This way I could use an IoC to inject my own IUnitOfWork to my repository classes transparently, and I could use the same instance to query and then update my entities.

Since I am using Unity, I mistakenly used PerThreadLifeTimeManager. I soon realised that ASP.Net threading model does not support what I want to achieve. Basically it uses a threadpool and recycles threads, and that means that I get one UnitOfWork per thread!! However, what I wanted was one unit of work per web request.

A bit of googling gave me this great post. That was exactly what I wanted; except for the unity part which was quite easy to achieve.

This is my implementation for PerCallContextLifeTimeManager for unity:

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()
    {
    }
}

And of course I use this to register my unit of work with a code similar to this:

unityContainer
            .RegisterType<IUnitOfWork, MyDataContext>(
            new PerCallContextLifeTimeManager(),
            new InjectionConstructor());

Hope it saves someone a bit of time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

贱人配狗天长地久 2024-08-02 23:19:38

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

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

否则,如果您有多个向 PerCallContextLifeTimeManager 注册的对象,它们将共享相同的键来访问 CallContext,并且您将无法获得预期的对象后退。

还值得实现RemoveValue来确保对象被清理:

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

Neat solution, but each instance of LifetimeManager should use a unique key rather than a constant:

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

Otherwise if you have more than one object registered with PerCallContextLifeTimeManager, they're sharing the same key to access CallContext, and you won't get your expected object back.

Also worth implementing RemoveValue to ensure objects are cleaned up:

public override void RemoveValue()
{
     CallContext.FreeNamedDataSlot(_key);
}
源来凯始玺欢你 2024-08-02 23:19:38

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

While this is fine calling this PerCallContextLifeTimeManager, I'm pretty sure this is not "safe" to be considered an ASP.Net Per-request LifeTimeManager.

If ASP.Net does its thread-swap then the only thing taken across to the new thread through CallContext is the current HttpContext - anything else you store in CallContext will be gone. This means under heavy load the code above could have unintended results - and I imagine it would be a real pain to track down why!

The only "safe" way to do this is with HttpContext.Current.Items, or doing something like:

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()
    {
    }
}

This obviously means taking dependencies on System.Web :-(

Much more information on this available at:

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

咆哮 2024-08-02 23:19:38

感谢您的贡献,

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

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

目前,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 中进行了处理。 这取决于模块的执行顺序。

Thanks for your contribution,

But the question proposed implementation has two drawbacks, one of which is a serious bug as already stated by Steven Robbins in his answer and Micah Zoltu in a comment.

  1. Call context is not guaranteed to be preserved by Asp.Net for a single request. Under load, it can switch to another one, causing proposed implementation to break.
  2. It does not handle releasing of dependencies at request end.

Currently, Unity.Mvc Nuget package supplies a PerRequestLifetimeManager for doing the work. Do not forget to register its associated UnityPerRequestHttpModule in bootstrapping code otherwise dependencies releasing will not be handled either.

Using bootstrapping

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

or in web.config system.webServer/modules

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

It appears its current implementation is also suitable for web forms. And it does not even depend on MVC. Unfortunately, its assembly does, because of some other classes it contains.

Beware, in case you use some custom http module using your resolved dependencies, they may be already disposed in the module EndRequest. It depends on module execution order.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文