Поддерживает ли контейнер FunQ IoC разрешение на регистрацию всех типов? Что-то вроде этого:
IEnumerable<IFoo> foos = container.Resolve<IEnumerable<IFoo>>();
IEnumerable<IFoo> foos = container.ResolveAll<IFoo>();
Поддерживает ли контейнер FunQ IoC разрешение на регистрацию всех типов? Что-то вроде этого:
IEnumerable<IFoo> foos = container.Resolve<IEnumerable<IFoo>>();
IEnumerable<IFoo> foos = container.ResolveAll<IFoo>();
В 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.
Для тех из вас, кто хочет анти-шаблон, просто потому, что вы не хотите внедрять шаблон 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);
}
}
}
}