Unity中的Singleton Per Call上下文(Web请求)

Meh*_*ili 45 asp.net singleton unity-container unit-of-work lifetime

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

由于我使用Unity,我错误地使用了PerThreadLifeTimeManager.我很快意识到ASP.Net线程模型不支持我想要实现的目标.基本上它使用theadpool并回收线程,这意味着每个线程我得到一个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()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,我使用它来使用与此类似的代码注册我的工作单元:

unityContainer
            .RegisterType<IUnitOfWork, MyDataContext>(
            new PerCallContextLifeTimeManager(),
            new InjectionConstructor());
Run Code Online (Sandbox Code Playgroud)

希望能节省一些时间.

小智 25

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

private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid());
Run Code Online (Sandbox Code Playgroud)

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

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

public override void RemoveValue()
{
     CallContext.FreeNamedDataSlot(_key);
}
Run Code Online (Sandbox Code Playgroud)

  • +1事实上,它必须像六个人说的那样.如果您没有分配唯一键,则所有对象都在单个键下注册,并且事情变得混乱. (8认同)

Ste*_*ins 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()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

这显然意味着依赖于System.Web :-(

有关这方面的更多信息,请访问:

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