问题
I'm using Unity
for Dependency Injection. I implemented generic repository IRepository<T>
and IUnitOfWork
as per direction mentioned here
Now when I access Repository and UnitOfWork in Service layer using constructor injection, it adds the data in database but never closes connection with database.
UnitOfWork implements IDisposable
but that's never called since I'm not able to implement Using
pattern knowing the fact that Repository and UnitOfWork is being shared by other functions also.
container.RegisterType<IRMContext, RMContext>(new PerResolveLifetimeManager());
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());
So for each request from fiddler, it creates a new connection to database and never closes, ultimately it reaches to 100 or more and then starts failing!
I have added code here in dotnetfiddle - https://dotnetfiddle.net/2K0W48
Updating further based on my study. If you look into code, there is IUnitOfWork
implementation having BeginTransaction
method in that. If I donot use that in Service Layer, then everything works fine. Only 1 database connection is maintained. But if that's used, the connection associated with that transaction is never closed and keeps increasing.
回答1:
In the Unity architecture the object responsible to Dispose the created objects is the LifetimeManagers and its implementations. In the example that you are using, is the PerResolveLifetimeManager.
If you see at https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx, you will see that not every LifetimeManager disposes its objects. For Example:
TransientLifetimeManager: Does not call Dispose
ContainerControlledLifetimeManager: When the container is disposed, it calls the Dispose method of the objects.
HierarchicalLifetimeManager: When the container is disposed, it calls the Dispose method of the objects.
PerResolveLifetimeManager: Does not call Dispose.
PerThreadLifetimeManager: Does not call Dispose.
ExternallyControlledLifetimeManager: Does not call Dispose.
We can test this with the following code:
[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;
}
}
With this in mind, you have at least two options:
1 - Use/Create a LifetimeManager that knows about the execution environment and somehow register itself to dispose everything that it has created, for example PerRequestLifetimeManager (see https://msdn.microsoft.com/en-us/library/microsoft.practices.unity.perrequestlifetimemanager(v=pandp.30).aspx).
The PerRequestLifetimeManager stores all created instances in the HttpContext.Current.Items, and use a associated HttpModule called UnityPerRequestHttpModule to dispose everything at the end of the Request. See below how UnityPerRequestHttpModule dispose its objects.
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();
}
}
}
With this, the first possible solution to your problem is:
var container = new UnityContainer();
container.RegisterType<IUnitOfWork, UnitOfWork>(new PerRequestLifetimeManager());
and be sure to register the UnityPerRequestHttpModule (see https://msdn.microsoft.com/en-us/library/dn507440(v=pandp.30).aspx)
2 - The second option is to create a root container, register all transient services with HierarchicalLifetimeManager, create a child container for each execution context and dispose this child container at the end of the execution context. For example:
+ Root
-- Child 1
-- Uow1
-- Svc1
-- Child 2
-- Uow1
-- Svc2
A possible test is:
[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);
}
}
One of the advantages is that the integration between Unity and WebApi already works with child container:
public class UnityDependencyResolver : UnityDependencyScope, IDependencyResolver, IDependencyScope, IDisposable
{
public UnityDependencyResolver(IUnityContainer container) : base(container)
{
}
public IDependencyScope BeginScope()
{
return new UnityDependencyScope(base.Container.CreateChildContainer());
}
}
This solution contains a trick: if your forget to call the dispose on the child container, two things will happen:
1 - The Dispose method of the objects will never be called, see the test below;
[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 - A memory leak will be created. This happens because the constructor that is called when a child container is created, insert itself in a list in the parent. It creates a tree where the parent points to the children and each child point back to the parent. This circle is only broken in the 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);
}
...
}
}
来源:https://stackoverflow.com/questions/39954586/unitofwork-repository-database-connection-issue