在Unity中,每个调用上下文(Web请求)都是单例的

时间:2021-09-19 16:57:14

A few days ago I had this 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.

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

Since I am using Unity, I mistakenly used PerThreadLifeTimeManager. I soon realised that ASP.Net threading model does not support what I want to acheive. Basically it uses a theadpool 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.

因为我在使用Unity,所以我错误地使用了PerThreadLifeTimeManager。我很快意识到ASP。Net线程模型不支持我想要的。基本上,它使用一个theadpool并回收线程,这意味着我每一个线程得到一个工作单元!然而,我想要的是每个web请求的一个工作单元。

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 acheive.

有一点谷歌搜索给了我这个伟大的职位。这正是我想要的;除了统一的部分,这很容易实现。

This is my implementation for PerCallContextLifeTimeManager for unity:

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

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.

希望它能节省一些时间。

3 个解决方案

#1


25  

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

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

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.

否则,如果在PerCallContextLifeTimeManager上注册了多个对象,那么它们将共享相同的键来访问CallContext,您将无法获得预期的对象。

Also worth implementing RemoveValue to ensure objects are cleaned up:

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

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

#2


20  

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

尽管调用PerCallContextLifeTimeManager是没问题的,但我敢肯定这不是一个“安全”的ASP。净每请求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!

如果ASP。Net执行它的线程交换,然后通过CallContext传递到新线程的唯一东西就是当前的HttpContext——你在CallContext中存储的任何其他东西都将消失。这意味着,在繁重的负载下,上面的代码可能会产生意想不到的结果——我想,找到原因将是一件非常痛苦的事情!

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

唯一的“安全”方法是使用HttpContext.Current。项目,或做类似的事情:

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

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

#3


3  

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.

但是,这个问题的提出有两个缺点,其中一个问题就像Steven Robbins在他的回答中提到的,Micah Zoltu在评论中提到的。

  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 breaks.
  2. 调用上下文不能保证由asp.net为单个请求保留。在负载下,它可以切换到另一个,导致建议的实现中断。
  3. It does not handle releasing of dependencies at request end.
  4. 它不处理在请求端释放依赖项。

Currently, Unity.Mvc Nuget package supply 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.

目前,团结。Mvc Nuget包提供一个PerRequestLifetimeManager来完成这项工作。不要忘记在引导代码中注册它的相关联的unityperrequesthpmodule,否则依赖性释放也不会被处理。

Using bootstrapping

使用引导

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

or in web.config system.webServer/modules

或在web。配置system.webServer /模块

<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, cause of some other classes it contains.

它的当前实现似乎也适用于web表单。它甚至不依赖于MVC。不幸的是,它的程序集产生了它所包含的其他类。

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.

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

#1


25  

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

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

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.

否则,如果在PerCallContextLifeTimeManager上注册了多个对象,那么它们将共享相同的键来访问CallContext,您将无法获得预期的对象。

Also worth implementing RemoveValue to ensure objects are cleaned up:

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

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

#2


20  

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

尽管调用PerCallContextLifeTimeManager是没问题的,但我敢肯定这不是一个“安全”的ASP。净每请求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!

如果ASP。Net执行它的线程交换,然后通过CallContext传递到新线程的唯一东西就是当前的HttpContext——你在CallContext中存储的任何其他东西都将消失。这意味着,在繁重的负载下,上面的代码可能会产生意想不到的结果——我想,找到原因将是一件非常痛苦的事情!

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

唯一的“安全”方法是使用HttpContext.Current。项目,或做类似的事情:

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

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

#3


3  

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.

但是,这个问题的提出有两个缺点,其中一个问题就像Steven Robbins在他的回答中提到的,Micah Zoltu在评论中提到的。

  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 breaks.
  2. 调用上下文不能保证由asp.net为单个请求保留。在负载下,它可以切换到另一个,导致建议的实现中断。
  3. It does not handle releasing of dependencies at request end.
  4. 它不处理在请求端释放依赖项。

Currently, Unity.Mvc Nuget package supply 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.

目前,团结。Mvc Nuget包提供一个PerRequestLifetimeManager来完成这项工作。不要忘记在引导代码中注册它的相关联的unityperrequesthpmodule,否则依赖性释放也不会被处理。

Using bootstrapping

使用引导

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));

or in web.config system.webServer/modules

或在web。配置system.webServer /模块

<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, cause of some other classes it contains.

它的当前实现似乎也适用于web表单。它甚至不依赖于MVC。不幸的是,它的程序集产生了它所包含的其他类。

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.

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