Поддерживает ли Funq ResolveAll?

Поддерживает ли контейнер FunQ IoC разрешение на регистрацию всех типов? Что-то вроде этого:

IEnumerable<IFoo> foos = container.Resolve<IEnumerable<IFoo>>();
IEnumerable<IFoo> foos = container.ResolveAll<IFoo>();

Ответ 1

В Funq нет метода ResolveAll, но вы можете просто зарегистрировать IEnumerable<IFoo> и разрешить его с помощью Resolve<IEnumerable<IFoo>>() как показано в вашем вопросе.

В целом, однако, лучше не запрашивать контейнер для коллекций, а вместо этого использовать композиты. Таким образом, вы можете просто внедрить IFoo в качестве зависимости, вместо того, чтобы заставлять потребителей этой зависимости повторять список. Вместо этого вы встраиваете код, который зацикливает список экземпляров IFoo внутри композита. Это делает ваш код СУХИМЫМ и не вынуждает вас проходить через (возможные) десятки операторов foreach (var foo in foos) разбросанных по всему приложению, когда необходимо внести изменения в способ итерации элементов. Или позвольте мне выразить это иначе: потребитель не обязан знать, как перебирать все IFoo.

Вот пример IFoo Composite:

// A composite is something that implements an interface
// (in this case IFoo) and wraps a list of items of that
// same interface.
public class FooComposite : IFoo
{
    private readonly IEnumerable<IFoo> foos;

    public FooComposite(params IFoo[] foos)
    {
        this.foos = foos;
    }

    void IFoo.FooThatThing(IBar bar)
    {
        foreach (var foo in this.foos)
        {
            foo.FooThatThing(bar);
        }
    }
}

Вместо регистрации IEnumerable<IFoo> вы можете зарегистрировать CompositeFoo как IFoo:

container.Register<IFoo>(c => new CompositeFoo(
    new Foo1(), new Foo2(), new Foo3()));

Теперь вы можете позволить контейнеру внедрить этот CompositeFoo в потребителей, которые принимают аргумент IFoo, что заставляет их не знать, что они на самом деле имеют дело со списком элементов IFoo.

ОБНОВЛЕНИЕ:

Используя этот составной шаблон, вы можете легко управлять временем жизни каждого элемента IFoo. Это просто вопрос обратного вызова в контейнер. С Funq это выглядело бы так:

container.Register<IFoo>(c => new CompositeFoo(
    c.Resolve<Foo1>(),
    c.Resolve<Foo2>(),
    c.Resolve<Foo3>()));

Таким образом, вы можете зарегистрировать Foo1 как синглтон и Foo2 как переходный, например. Однако, когда CompositeFoo используется повторно, Foo2 самом деле не будет переходным, но это просто вопрос изменения CompositeFoo и его регистрации для решения этой проблемы. Например, вы можете изменить свой CompositeFoo на следующее:

public class FooComposite : IFoo
{
    private readonly Func<IFoo>[] fooFactories;

    public FooComposite(params Func<IFoo>[] fooFactories)
    {
        this.fooFactories = fooFactories;
    }

    void IFoo.FooThatThing(IBar bar)
    {
        foreach (var fooFactory in this.fooFactories)
        {
            var foo = fooFactory();

            foo.FooThatThing(bar);
        }
    }
}

Теперь вместо того, чтобы вводить IFoo в конструктор, мы можем IFoo в него несколько IFoo:

container.Register<IFoo>(c => new CompositeFoo(
    () => c.Resolve<Foo1>(),
    () => c.Resolve<Foo2>(),
    () => c.Resolve<Foo3>()));

Это гарантирует, что каждый раз, FooThatThing вызывается CompositeFoo FooThatThing, контейнер запрашивается для новых экземпляров IFoo. Это позволяет FooThatThing вызываться несколько раз одним и тем же потребителем и даже позволяет регистрировать CompositeFoo как singleton.

Этот совет относится ко всем контейнерам и внедрению зависимостей в целом и не относится к использованию Funq.

Ответ 2

Для тех из вас, кто хочет анти-шаблон, просто потому, что вы не хотите внедрять шаблон factory, вот моя версия, основанная на последнем 3.9.71.

Единственным недостатком является то, что он добавляет немного больше памяти, и вы должны регистрировать службы через registerOneOfMany(). вам может потребоваться изменить сумку для списка с блокировкой (для более быстрого чтения) и переключиться на servicstack funq по умолчанию: ReuseScope.Default

public static class FunqExtensions
{
    private static readonly ConcurrentDictionary<Type,ConcurrentBag<string>> registrations = new ConcurrentDictionary<Type, ConcurrentBag<string>>();

    public static void RegisterOneOfMany<TBase, TImplementation>(this Container container, string name = null, ReuseScope scope  = ReuseScope.None) where TImplementation : TBase
    {
        if (name == null)
            name = Guid.NewGuid().ToString();

        var funq = Container.GenerateAutoWireFn<TImplementation>();
        container.Register<TBase>(name, (c) => funq(c))
          .ReusedWithin(scope);
        registrations.GetOrAdd(typeof(TBase), type => new ConcurrentBag<string>()).Add(name);
    }

    public static IEnumerable<T> ResolveAll<T>(this Container container)
    {
        ConcurrentBag<string> result;
        if (registrations.TryGetValue(typeof(T), out result))
        {
            var rator = result.GetEnumerator();
            while (rator.MoveNext())
            {
                yield return container.ResolveNamed<T>(rator.Current);
            }
        }
    }  
}