Подтвердить что ты не робот

Вставка 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:

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

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

4b9b3361

Ответ 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; }

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