Castle Windsor (или другой DI) - создание объекта на основе параметра - PullRequest
1 голос
/ 06 октября 2009

Я довольно новичок во всем, что касается DI / IoC, так что терпите меня ...

У меня есть такая настройка:

interface IA
interface IB
interface IC
abstract class A : IA
class B : A, IB
class C : A, IC

interface IX
interface IY
interface IZ
abstract class X : IX
class Y : X, IY
class Z : X, IZ

Конструкторы B и C выглядят так:

public B(IY y);
public C(IZ z);

Теперь я хочу, чтобы B или C строились на основе уже созданного экземпляра Y или Z. Как это:

IX x = new ...; // either Y or Z, determined at runtime
// lots of code
IA a = fancyfuncoftruth<IA>(x); // creates an instance of either B or C, depending on x

Возможно ли что-то подобное?

Чтобы дать вам представление: я пытаюсь объединить древовидную структуру WPF, шаблон MVVM и DI.

Спасибо за ваше время.

Ответы [ 2 ]

2 голосов
/ 06 октября 2009

Я не совсем уверен, понимаю ли я то, что вы ищете, но мне кажется, что вы спрашиваете, есть ли какая-либо функция, которая может правильно разрешать IA на основе определенного значения IX (x).

Лучше всего реализовать это, используя Abstract Factory , который отображает экземпляры IX на IA.

Я бы лично реализовал это как собственную абстрактную фабрику, но вы также можете использовать UsingFactory или UsingFactoryMethod из Castle Windsor:

IX x = new ...;

var container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<IA>().UsingFactoryMethod(k =>
    {
        // Do fancy stuff with x here
        // This example just shows that x can be referenced
        // in the closure, but I'm not using it...
        if (x == null)
        {
        }
        return k.Resolve<B>();
    }));
container.Register(Component.For<B>());
container.Register(Component.For<IY>().ImplementedBy<Y>());

var result = container.Resolve<IA>();
0 голосов
/ 13 октября 2009

Пфех, я нашел ответ. Наверное, не самый лучший, но хоть что-то для начала.

Посмотрите на следующий полный пример: (для этого я использовал NInject):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Parameters;
using Ninject.Conditions;

namespace IoCTest01
{
    interface IA { }
    interface IB : IA { }
    interface IC : IA { }

    abstract class A : IA { }

    class B : A, IB
    {
        public B(IY x)
        {
            Console.WriteLine("Constructor for B called!");
        }
    }

    class C : A, IC
    {
        public C(IZ x)
        {
            Console.WriteLine("Constructor for C called!");
        }
    }

    interface IX { }
    interface IY : IX { }
    interface IZ : IX { }

    abstract class X : IX { }

    class Y : X, IY
    {
    }
    class Z : X, IZ
    {
    }

    class TestModule : StandardModule
    {
        public override void Load()
        {
            Bind<IY>().To<Y>();
            Bind<IZ>().To<Z>();

            Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                e =>
                {
                    return e.Value.GetType().Equals(typeof(Y));
                }));
            Bind<IA>().To<C>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                e =>
                {
                    return e.Value.GetType().Equals(typeof(Z));
                }));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IKernel kernel = new StandardKernel(new TestModule());

            IX x1 = kernel.Get<IY>();
            IX x2 = kernel.Get<IZ>();

            kernel.Dispose();

            // lots of code

            kernel = new StandardKernel(new TestModule());

            var parameters = new ParameterCollection();
            parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x1));
            kernel.Get<IA>(parameters);

            parameters = new ParameterCollection();
            parameters.Add<ConstructorArgumentParameter>(new ConstructorArgumentParameter("x", x2));
            kernel.Get<IA>(parameters);
        }
    }
}

Когда он запущен, он отображает:

Constructor for B called!
Constructor for C called!

Сначала я должен был вручную внедрить уже созданные объекты x1 и x2 в ядро. Get-вызовы. Похоже, этого было достаточно, чтобы NInject разрешил правильную сущность, но как только я добавил вторую привязку для IA, он пожаловался на множественные привязки по умолчанию для IA. Поэтому мне пришлось сделать некоторую контекстную привязку:

Bind<IA>().To<B>().Only(When.Context.Parameter<ConstructorArgumentParameter>("x").Matches(
                    e =>
                    {
                        return e.Value.GetType().Equals(typeof(Y));
                    }));

Проверяет, имеет ли параметр x тип Y. Если да, используется эта привязка.

Опять же, хотя это решение, оно, вероятно, далеко не оптимально. Я хотел, чтобы NInject мог разрешить правильный тип (B или C) для создания экземпляра из динамического типа (Y или Z) параметра (x).

Ах, хорошо. : -)

У кого-нибудь есть лучшее решение?

To Mark: код лучше объясняет проблему? Основной метод должен дать вам обзор.

...