Вставка Setter/property в Unity без атрибутов
Я работаю над проектом, в котором инфраструктура Unity используется как контейнер IoC. Мой вопрос связан с инъекцией необязательной зависимости (в данном случае регистратора) на несколько классов с использованием инъекции свойств или сеттеров.
Я не хочу загромождать конструкторы всех моих классов с этими необязательными зависимостями, но я не могу найти хороший способ справиться с этим в Unity. Как вы это сделаете, в соответствии с документацией MSDN добавив атрибут свойства:
private ILogger logger;
[Dependency]
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
Я считаю это очень уродливым. В StructureMap можно было сделать следующее, чтобы установить все свойства данного типа:
SetAllProperties(policy => policy.OfType<ILog>());
Кто-нибудь знает, можно ли сделать что-то подобное в Unity?
Edit:
Ким Майор предлагает использовать этот подход, который также может быть достигнут с помощью кода.
Мне бы интересны примеры того, как это сделать автоматически для всех подходящих свойств.
Ответы
Ответ 1
Мне тоже не нравятся эти атрибуты
Вы можете использовать все с помощью метода Настроить контейнера единиц измерения:
Сначала зарегистрируйте тип
unityContainer.RegisterType<MyInterface,MyImpl>(
new ContainerControlledLifetimeManager());
Если у вас есть несколько конструкторов, вам придется это сделать, поэтому Unity вызывает конструктор без параметров (если ни один из них не будет установлен Unity для самого толстого)
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionConstructor());
Настройка зависимостей свойств
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionProperty(
"SomePropertyName",
new ResolvedParameter<MyOtherInterface>()));
Настройка зависимости метода
unityContainer.Configure<InjectedMembers>()
.ConfigureInjectionFor<MyImpl>(
new InjectionMethod(
"SomeMethodName",
new ResolvedParameter<YetAnotherInterface>()));
Ответ 2
Я тоже не большой поклонник использования атрибутов, но мне также не нравится метод .Configure<InjectedMembers>()
, потому что вы привязаны к определенному имени свойства и конкретному значению. То, как я нашел, что дает вам максимальную гибкость, - это создать собственную стратегию строителя.
Я создал этот простой класс, который выполняет итерации свойств создаваемого объекта и устанавливает его значения свойств, если тип этого свойства был зарегистрирован в контейнере единиц.
public class PropertyInjectionBuilderStrategy:BuilderStrategy
{
private readonly IUnityContainer _unityContainer;
public PropertyInjectionBuilderStrategy(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public override void PreBuildUp(IBuilderContext context)
{
if(!context.BuildKey.Type.FullName.StartsWith("Microsoft.Practices"))
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(context.BuildKey.Type);
foreach (PropertyDescriptor property in properties)
{
if(_unityContainer.IsRegistered(property.PropertyType)
&& property.GetValue(context.Existing) == null)
{
property.SetValue(context.Existing,_unityContainer.Resolve(property.PropertyType));
}
}
}
}
}
Вы регистрируете свой BuilderStrategy
, создавая UnityContainerExtension
. Вот пример:
public class TestAppUnityContainerExtension:UnityContainerExtension
{
protected override void Initialize()
{
Context.Strategies.Add(new PropertyInjectionBuilderStrategy(Container), UnityBuildStage.Initialization);
}
}
Это регистрируется в контейнере Unity как таковой:
IUnityContainer container = new UnityContainer();
container.AddNewExtension<TestAppUnityContainerExtension>();
Надеюсь, что это поможет,
Matthew
Ответ 3
Исходный пример, который вы опубликовали, выглядит очень громоздким, но вы можете использовать автоматически реализованные свойства, подобные этому, чтобы очистить этот код:
[Dependency]
public ILogger Logger { get; set; }
Ответ 4
Вы можете попробовать следующее:
этот код в MyClass
[InjectionMethod]
public void Initialize(
[Dependency] ILogger logger
а затем называя его:
unitycontainer.BuildUp<MyClass>(new MyClass());
единицу будет вызывать метод Initialize с зависимостью от контейнера, а затем u может сохранить его в частной переменной в MyClass или что-то в этом роде.
Ответ 5
С Unity 2.1 это тоже сработает:
var container = new UnityContainer()
.RegisterType<ILogger, Logger>()
.RegisterType<ISomeInterface, SomeImplementaion>(
new InjectionProperty("Logger", new ResolvedParameter<ILogger>()));
Введенное свойство класса SomeImplementaion просто
public ILogger Logger { get; set; }
Ответ 6
В следующем пошаговом руководстве показан один из способов сделать это через конфигурацию. Вы можете, конечно, пронести его и с помощью кода.
http://aardvarkblogs.wordpress.com/unity-container-tutorials/10-setter-injection/
Ответ 7
Посмотрите на проект UnityConfiguration для конфигурации на основе соглашения. Он работает очень хорошо, хотя у меня проблема при поиске нескольких реализаций с помощью ResolveAll<IType>()
. См. этот вопрос.
container.RegisterInstance(typeof (ILogger), LoggerService.GetLogger());
container.Configure(c => c.Scan(scan =>
{
scan.AssembliesInBaseDirectory(a => a.FullName.StartsWith("My.Company")); // Filter out everthing that are not from my assemblies
scan.InternalTypes();
scan.With<SetAllPropertiesConvention>().OfType<ILogger>();
}));
Ответ 8
Вы можете ввести свой класс в контейнер с единством.
например, если у вас есть класс "MyClass", и у вас есть зависимость от двух интерфейсов "IExample1" и "IExample2", и вы не хотите определять параметр для них в конструкторе, а затем выполните следующие шаги
Шаг 1. Зарегистрируйте оба интерфейса в контейнере единиц
IUnityContainer container = new UnityContainer();
container.RegisterType<IExample1,Impl1>();
container.RegisterType<IExample2,Impl2>();
//resolve class MyClass
var myClass = container.Resolve<MyClass>();
Шаг 2. Ваш класс должен выглядеть следующим образом
public class MyClass
{
IExample1 ex1;
IExample2 ex2
public MyClass(IUnityContainer container)
{
/* This unity container is same unity container that we used in step
1 to register and resolve classes. So you can use this container to resolve
all the dependencies. */
ex1= container.Resolve<IExample1>(); // will give you object of Impl1
ex2= container.Resolve<IExample2>(); // will give you object of Impl2
}
}
Шаг 3. Таким образом, вы можете разрешить любое количество зависимостей, не определяя их в конструкторе.