UnitOfWork,存储库数据库连接问题

时间:2016-10-10 08:53:32

标签: design-patterns asp.net-web-api2 repository-pattern unit-of-work

我使用Unity进行依赖注入。我按照提到的方向[{3}}

实施了通用存储库IRepository<T>IUnitOfWork

现在,当我使用构造函数注入访问Service层中的Repository和UnitOfWork时,它会在数据库中添加数据,但从不关闭与数据库的连接。

UnitOfWork实现IDisposable但是从未调用过,因为我无法实现Using模式,知道Repository和UnitOfWork也被其他函数共享。< / p>

container.RegisterType<IRMContext, RMContext>(new PerResolveLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());

因此,对于来自fiddler的每个请求,它会创建一个与数据库的新连接,并且永远不会关闭,最终达到100或更多,然后开始失败!

我在dotnetfiddle中添加了代码 - here

根据我的研究进一步更新。如果您查看代码,那么IUnitOfWork实现具有BeginTransaction方法。如果我不在服务层中使用它,那么一切正常。仅维护1个数据库连接。但是如果使用了这个,那么与该交易相关的连接永远不会关闭并且不断增加。

1 个答案:

答案 0 :(得分:1)

在Unity体系结构中,负责处理已创建对象的对象是LifetimeManagers及其实现。在您使用的示例中,是PerResolveLifetimeManager。

如果你看到了 https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx,您将看到并非每个LifetimeManager都会处理其对象。例如:

TransientLifetimeManager :不调用Dispose
ContainerControlledLifetimeManager :当处置容器时,它会调用对象的Dispose方法。
HierarchicalLifetimeManager :当处置容器时,它会调用对象的Dispose方法。
PerResolveLifetimeManager :不调用Dispose PerThreadLifetimeManager :不调用Dispose ExternallyControlledLifetimeManager :不调用Dispose。

我们可以使用以下代码对此进行测试:

[TestMethod]
public void PerResolveLifetimeManagerDoesNotCallDispose()
{
    var container = new UnityContainer();
    container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());

    var uow = (UnitOfWork)container.Resolve<IUnitOfWork>();

    Assert.IsFalse(uow.Disposed);

    container.Dispose();

    Assert.IsFalse(uow.Disposed);
}

public interface IUnitOfWork : IDisposable
{
}

public class UnitOfWork : IUnitOfWork
{
    public bool Disposed { get; set; }

    public void Dispose()
    {
        Disposed = true;
    }
}

考虑到这一点,您至少有两个选择:

1 - 使用/创建一个了解执行环境的LifetimeManager,并以某种方式注册自己以处置它创建的所有内容,例如PerRequestLifetimeManager(参见https://msdn.microsoft.com/en-us/library/microsoft.practices.unity.perrequestlifetimemanager(v=pandp.30).aspx)。

PerRequestLifetimeManager将所有创建的实例存储在HttpContext.Current.Items中,并使用名为UnityPerRequestHttpModule的关联HttpModule来处理请求末尾的所有内容。请参阅下文UnityPerRequestHttpModule如何处置其对象。

private void OnEndRequest(object sender, EventArgs e)
{
    HttpApplication httpApplication = (HttpApplication)sender;
    Dictionary<object, object> dictionary = UnityPerRequestHttpModule.GetDictionary(httpApplication.Context);
    if (dictionary != null)
    {
        foreach (IDisposable current in dictionary.Values.OfType<IDisposable>())
        {
            current.Dispose();
        }
    }
}

有了这个,您问题的第一个可能的解决方案是:

var container = new UnityContainer();
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());

并确保注册UnityPerRequestHttpModule(请参阅https://msdn.microsoft.com/en-us/library/dn507440(v=pandp.30).aspx

2 - 第二个选项是创建根容器,使用HierarchicalLifetimeManager注册所有临时服务,为每个执行上下文创建子容器,并在执行上下文的末尾处置此子容器。例如:

+ Root
    -- Child 1
        -- Uow1
        -- Svc1
    -- Child 2
        -- Uow1
        -- Svc2

可能的测试是:

[TestMethod]
public void WithChild()
{
    var container = new UnityContainer();
    container.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager());

    for (int i = 0; i < 10; ++i)
    {
        var child = container.CreateChildContainer();
        var uow = (UnitOfWork)child.Resolve<IUnitOfWork>();

        Assert.IsFalse(uow.Disposed);

        child.Dispose();

        Assert.IsTrue(uow.Disposed);
    }
}

其中一个优点是Unity和WebApi之间的集成已经可以与子容器一起使用:

public class UnityDependencyResolver : UnityDependencyScope, IDependencyResolver, IDependencyScope, IDisposable
{
    public UnityDependencyResolver(IUnityContainer container) : base(container)
    {
    }

    public IDependencyScope BeginScope()
    {
        return new UnityDependencyScope(base.Container.CreateChildContainer());
    }
}

这个解决方案包含一个技巧:如果忘记在子容器上调用dispose,将会发生两件事: 1 - 永远不会调用对象的Dispose方法,请参阅下面的测试;

[TestMethod]
public void DoNotForgetToCallTheDispose()
{
    var container = new UnityContainer();
    container.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager());

    List<UnitOfWork> objects = new List<UnitOfWork>();

    for (int i = 0; i < 10; ++i)
    {
        var child = container.CreateChildContainer();
        var uow = (UnitOfWork)child.Resolve<IUnitOfWork>();
        objects.Add(uow);

        Assert.IsFalse(uow.Disposed);
    }

    Assert.IsTrue(objects.All(x => x.Disposed)); // Will throw!
}

2 - 将创建内存泄漏。发生这种情况是因为创建子容器时调用的构造函数将其自身插入父级的列表中。它创建了一个树,其中父级指向子级,每个子级指向父级。这个圆圈只在Child Dispose中被打破。

private UnityContainer(UnityContainer parent)
{
    this.parent = parent;
    if (parent != null)
    {
        parent.lifetimeContainer.Add(this);
    }
...
}

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        ...
            if (this.parent != null && this.parent.lifetimeContainer != null)
            {
                this.parent.lifetimeContainer.Remove(this);
            }
        ...
    }
}