0

Я использую Unity для инъекций зависимостей. Я реализовал общий репозиторий IRepository<T> и IUnitOfWork согласно направлению упоминаются hereUnitOfWork, Ошибка подключения базы данных хранилища

Теперь, когда я получить доступ к хранилищу и UnitOfWork в слое обслуживания с помощью инъекции конструкторы, он добавляет данные в базе данных, но никогда не закрывает соединение с базой данных.

UnitOfWork реализует IDisposable, но это никогда не вызывается, так как я не могу реализовать шаблон Using, зная тот факт, что репозиторий и UnitOfWork совместно используются другими функциями.

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

Таким образом, для каждого запроса от скрипача, он создает новое подключение к базе данных и никогда не закрывается, в конечном счете, доходит до 100 и более, а затем начинает сбиваться!

Я добавил код здесь в dotnetfiddle - https://dotnetfiddle.net/2K0W48

Обновление далее на основе моего исследования. Если вы посмотрите на код, есть IUnitOfWork реализация, имеющая BeginTransaction метод в этом. Если я не буду использовать это в Service Layer, тогда все будет хорошо. Поддерживается только 1 соединение с базой данных. Но если это используется, соединение, связанное с этой транзакцией, никогда не закрывается и продолжает увеличиваться.

+0

использовать одноразовые единицы работы, которые являются временными. иметь фабрику единиц продукции, которая дает вам единицу работы, когда это необходимо, чтобы они могли быть удалены без влияния на другие модули. – Nkosi

+0

@Nkosi, вы можете объяснить больше? Как я уже упоминал, я уже применил одноразовое для unitOfWork, но он никогда не вызывается из-за IOC ... –

+0

https://msdn.microsoft.com/en-us/library/ff660872(v=pandp.20).aspx – Nkosi

ответ

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.Элементы и используйте связанный 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); 
      } 
     ... 
    } 
} 
+0

Это подробное и точное объяснение, которое может дать любой человек. Огромное спасибо. –

+0

Frederico Lins, что произойдет, если мои сервисы PerResolveLifetimeManager и IUnitOfWork & DBConext являются PerRequestLifeTimeManager? (Я не удаляю явно или не вызываю dispose в Request_End) –

+0

Если ваши сервисы PerResolve и вы никогда не вызываете dispose, они никогда не удаляются напрямую. Если ваш IUnitOfWork и DBContext являются PerRequest И вы настроили UnityPerRequestHttpModule, они расположены автоматически, –

Смежные вопросы