Я знаю, что подобный вопрос задавался несколько раз (например: здесь , здесь , здесь и здесь ), но это был для предыдущих версий Unity, где ответ зависел от используемого LifetimeManager
класса.
Документация гласит:
Unity использует определенные типы, которые наследуют
из базового класса LifetimeManager
(в совокупности упоминается как пожизненная
менеджеры) контролировать, как он хранит
ссылки на экземпляры объектов и как
контейнер избавляется от этих
экземпляры.
Хорошо, звучит хорошо, поэтому я решил проверить реализацию встроенных менеджеров жизни. Мой вывод:
TransientLifetimeManager
- без утилизации. Контейнер разрешает только экземпляр и не отслеживает его. Телефонный код отвечает за удаление экземпляра.
ContainerControlledLifetimeManager
- удаляет экземпляр, когда диспетчер времени жизни удаляется (= когда контейнер утилизируется). Предоставляет единичный экземпляр, общий для всех контейнеров в hiearchy.
HierarchicalLifetimeManager
- выводит поведение из ContainerControlledLifetimeManager
. Он предоставляет «одиночный» экземпляр для каждого контейнера в hiearchy (подконтейнеры).
ExternallyControlledLifetimeManager
- без утилизации. Правильное поведение, поскольку контейнер не является владельцем экземпляра.
PerResolveLifetimeManager
- без утилизации. Как правило, он такой же, как TransientLifetimeManager
, но позволяет повторно использовать экземпляр для внедрения зависимости при разрешении всего графа объекта.
PerThreadLifetimeManager
- нет обработки утилизации, как также описано в MSDN. Кто отвечает за утилизацию?
Реализация встроенного PerThreadLifetimeManager
- это:
public class PerThreadLifetimeManager : LifetimeManager
{
private readonly Guid key = Guid.NewGuid();
[ThreadStatic]
private static Dictionary<Guid, object> values;
private static void EnsureValues()
{
if (values == null)
{
values = new Dictionary<Guid, object>();
}
}
public override object GetValue()
{
object result;
EnsureValues();
values.TryGetValue(this.key, out result);
return result;
}
public override void RemoveValue()
{ }
public override void SetValue(object newValue)
{
EnsureValues();
values[this.key] = newValue;
}
}
Таким образом, одноразовый контейнер не удаляет одноразовые экземпляры, созданные с помощью этого менеджера времени жизни. Завершение потока также не удалит эти экземпляры. Так кто же ответственен за выпуск инстансов?
Я попытался вручную найти разрешенный экземпляр в коде и обнаружил другую проблему. Я не могу разрушить инстинкт. RemoveValue менеджера времени жизни пуста - после создания экземпляра невозможно удалить его из статического словаря потока (я также подозреваю, что метод TearDown
ничего не делает). Поэтому, если вы позвоните Resolve
после утилизации экземпляра, вы получите удаленный экземпляр. Я думаю, что это может быть довольно большой проблемой при использовании этого менеджера времени жизни с потоками из пула потоков.
Как правильно использовать этот менеджер времени жизни?
Кроме того, эта реализация часто используется в пользовательских менеджерах времени жизни, таких как PerCallContext, PerHttpRequest, PerAspNetSession, PerWcfCall и т. Д. Только статический словарь потока заменяется какой-либо другой конструкцией.
Также правильно ли я понимаю, что обработка одноразовых предметов зависит от времени жизни менеджера? Таким образом, код приложения зависит от используемого менеджера времени жизни.
Я читал, что в других контейнерах IoC, имеющих дело с временными одноразовыми объектами, обрабатываются субконтейнеры, но я не нашел пример для Unity - его можно было бы обработать с локальным субконтейнером и HiearchicalLifetimeManager
, но я не уверен, как сделай это.