В архитектуре 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.Элементы и используйте связанный HttpModule под названием UnityPerRequestHttpModule, чтобы уничтожить все в конце Запроса. См. Ниже, как 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 объектов w плохого никогда не вызывать, см. тест ниже;
[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);
}
...
}
}
использовать одноразовые единицы работы, которые являются временными. иметь фабрику единиц продукции, которая дает вам единицу работы, когда это необходимо, чтобы они могли быть удалены без влияния на другие модули. – Nkosi
@Nkosi, вы можете объяснить больше? Как я уже упоминал, я уже применил одноразовое для unitOfWork, но он никогда не вызывается из-за IOC ... –
https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx – Nkosi