6

Я играл с Unity, чтобы понять его немного больше, и натолкнулся на следующий сценарий. Если бы я дважды регистрировал один и тот же тип, но один из них был одиночным, как я могу позвонить Resolve, чтобы вернуть только один сингл? Я понимаю, что это можно сделать с помощью уникального Name, но мне интересно, можно ли это сделать без него.Поведение единства для регистрации одного и того же типа дважды и разрешения только одноэлементного

Например:

_container.RegisterType<Music>(new InjectionConstructor(new Album("Non-singleton", "Non-singleton"))); 
_container.RegisterType<Music>(new ContainerControlledLifetimeManager()); 

и вызова

var music = Factory.Resolve<Music>(); 

возвращает 'Non-одиночки' объект. Сначала я подумал, что это было основано на регистрационном заказе, но, переключаясь, я все еще получаю экземпляр «Non-singleton».

Есть ли причина для этого? Есть ли способ для Resolve только одноэлементный тип для двух зарегистрированных объектов в одном контейнере без указания атрибута Name?

ответ

21

Давайте разбить его:

_container.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton"))); 

Это регистрируется в контейнере типа Music без имени (нуль или по умолчанию) и говорит Unity использовать конструктор, который принимает тип Album. Также всегда нужно передать экземпляр, созданный new Album("Non-singleton", "Non-singleton") как значение для конструктора Music(Album album). Поэтому каждый экземпляр Music будет иметь тот же альбом, который был введен в него.

_container.RegisterType<Music>(new ContainerControlledLifetimeManager()); 

Это выполняет регистрацию на основе типа Music без имени (нуль или по умолчанию). Потому что, нет указателей InjectionMembers, единственным изменением существующей регистрации является добавление менеджера продолжительности жизни. Текущая регистрация обновляется, потому что BuildKey (Тип: Музыка, Имя: null) одинаково для обоих вызовов RegisterType.

unityContainer.RegisterType<Music>(new InjectionConstructor(new Album("Non-singleton", "Non-singleton"))); 

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager()); 

var music = unityContainer.Resolve<Music>(); 
var music2 = unityContainer.Resolve<Music>(); 

bool areEqual = ReferenceEquals(music, music2); 

В вышеуказанных isEqual истинно, поэтому возвращается один экземпляр.

Есть ли способ разрешить только одноэлементный тип для двух зарегистрированных объектов в одном контейнере без указания атрибута имени?

типы регистрируются по типу и названию, так что единственный способ иметь несколько регистраций для того же типа, чтобы использовать имя:

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

unityContainer.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton"))); 

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager(), 
    new InjectionConstructor(new Album("singleton", "singleton"))); 

Как примечание стороны, вы, как правило, не трудно кода зависимость (в данном случае альбом) внутри типа регистра вызова, если это не было действительно постоянной величиной. Даже тогда вы можете захотеть зарегистрировать постоянный экземпляр как синглтон и позволить контейнеру разрешить его для вас.

Интересный вопрос: как бы вы сбросили политику выбора конструктора после первого вызова RegisterType, чтобы Unity использовала политику выбора конструктора по умолчанию. Вы можете сделать это с помощью специального InjectionMember, который очистит политику выбора конструктора:

unityContainer.RegisterType<Music>(new InjectionConstructor(
    new Album("Non-singleton", "Non-singleton"))); 

unityContainer.RegisterType<Music>(new ContainerControlledLifetimeManager(), 
    new ClearInjectionConstructor()); 

public class ClearInjectionConstructor : InjectionMember 
{ 
    public override void AddPolicies(Type serviceType, 
     Type implementationType, 
     string name, 
     Microsoft.Practices.ObjectBuilder2.IPolicyList policies) 
    { 
     policies.Clear<IConstructorSelectorPolicy>(
      new NamedTypeBuildKey(implementationType, name)); 
    } 
}