Я знаю, что аналогичный вопрос задавался несколько раз (например: здесь, здесь, здесь и здесь), но это было для предыдущих версий Unity, где ответ зависел от используемого класса LifetimeManager
.
Документация гласит:
Unity использует определенные типы, которые наследуют из базового класса LifetimeManager (в совокупности называемый пожизненным менеджеров), чтобы контролировать, как он хранит ссылки на экземпляры объектов и контейнер располагает этими экземпляры.
Хорошо, звучит хорошо, поэтому я решил проверить реализацию сборки в пожизненных менеджерах. Мой вывод:
-
TransientLifetimeManager
- никакой обработки утилизации. Контейнер разрешает только экземпляр, и он не отслеживает его. Код вызова отвечает за удаление экземпляра. -
ContainerControlledLifetimeManager
- предоставляет экземпляр, когда диспетчер времени жизни расположен (= при размещении контейнера). Предоставляет экземпляр singleton, общий для всех контейнеров в hiearchy. -
HierarchicalLifetimeManager
- выводит поведение изContainerControlledLifetimeManager
. Он предоставляет экземпляр "singleton" для каждого контейнера в 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
, но я не уверен, как сделайте это.