Внедрение сеттера / свойства в Unity без атрибутов - PullRequest
24 голосов
/ 05 февраля 2009

Я работаю над проектом, в котором платформа 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:

Ким Майор предлагает использовать этот подход , который также может быть реализован с помощью кода.

Мне было бы интересно узнать, как сделать это автоматически для всех подходящих свойств.

Ответы [ 8 ]

19 голосов
/ 11 февраля 2009

Мне тоже не нравятся эти атрибуты

Вы можете сделать все, используя Настроить метод контейнера Unity:

Сначала зарегистрируйте тип

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>()));
16 голосов
/ 04 февраля 2011

Я также не большой поклонник использования атрибутов, но мне также не нравится метод .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>();

Надеюсь, это поможет,
Мэтью

7 голосов
/ 27 ноября 2010

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

[Dependency]
public ILogger Logger { get; set; }
5 голосов
/ 26 марта 2012

С 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; }
4 голосов
/ 23 мая 2009

Вы можете попробовать это:

этот код в MyClass

[InjectionMethod]
public void Initialize(
                 [Dependency] ILogger logger

и затем вызывая его по:

unitycontainer.BuildUp<MyClass>(new MyClass());

unity будет вызывать метод Initialize с зависимостью от контейнера, а затем вы можете сохранить его в закрытой переменной в MyClass или что-то ...

3 голосов
/ 05 февраля 2009

В следующем пошаговом руководстве показан один из способов настройки. Конечно, вы можете подключить его через код. http://aardvarkblogs.wordpress.com/unity-container-tutorials/10-setter-injection/

1 голос
/ 25 апреля 2013

Посмотрите на проект 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>();
                    }));
0 голосов
/ 24 сентября 2016

Вы можете добавить свой класс в контейнер единиц.

например, если у вас есть класс «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 . Таким образом, вы можете разрешить любое количество зависимостей, не определяя их в конструкторе.

...