UnitOfWork,存储库数据库连接问题
UnitOfWork, Repository Database Connection Issue
我正在使用 Unity
进行依赖注入。我按照提到的方向 here
实现了通用存储库 IRepository<T>
和 IUnitOfWork
现在,当我使用构造函数注入访问服务层中的 Repository 和 UnitOfWork 时,它会在数据库中添加数据,但不会关闭与数据库的连接。
UnitOfWork 实现 IDisposable
但从未调用过,因为我无法实现 Using
模式,因为我知道 Repository 和 UnitOfWork 也被其他函数共享。
container.RegisterType<IRMContext, RMContext>(new PerResolveLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());
因此对于来自 fiddler 的每个请求,它都会创建一个新的数据库连接并且永远不会关闭,最终它达到 100 或更多然后开始失败!
我在 dotnetfiddle 中添加了代码 - https://dotnetfiddle.net/2K0W48
根据我的研究进一步更新。如果您查看代码,就会发现 IUnitOfWork
实现中有 BeginTransaction
方法。如果我不在 服务层 中使用它,那么一切正常。仅维护 1 个数据库连接。但如果使用它,与该事务关联的连接永远不会关闭并不断增加。
在Unity架构中,负责Dispose创建对象的对象是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 - Use/Create 了解执行环境并以某种方式注册自身以处置其创建的所有内容的 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());
}
}
此解决方案包含一个技巧:如果您忘记调用子容器上的处置,将会发生两件事:
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);
}
...
}
}
我正在使用 Unity
进行依赖注入。我按照提到的方向 here
IRepository<T>
和 IUnitOfWork
现在,当我使用构造函数注入访问服务层中的 Repository 和 UnitOfWork 时,它会在数据库中添加数据,但不会关闭与数据库的连接。
UnitOfWork 实现 IDisposable
但从未调用过,因为我无法实现 Using
模式,因为我知道 Repository 和 UnitOfWork 也被其他函数共享。
container.RegisterType<IRMContext, RMContext>(new PerResolveLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());
因此对于来自 fiddler 的每个请求,它都会创建一个新的数据库连接并且永远不会关闭,最终它达到 100 或更多然后开始失败!
我在 dotnetfiddle 中添加了代码 - https://dotnetfiddle.net/2K0W48
根据我的研究进一步更新。如果您查看代码,就会发现 IUnitOfWork
实现中有 BeginTransaction
方法。如果我不在 服务层 中使用它,那么一切正常。仅维护 1 个数据库连接。但如果使用它,与该事务关联的连接永远不会关闭并不断增加。
在Unity架构中,负责Dispose创建对象的对象是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 - Use/Create 了解执行环境并以某种方式注册自身以处置其创建的所有内容的 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());
}
}
此解决方案包含一个技巧:如果您忘记调用子容器上的处置,将会发生两件事:
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);
}
...
}
}