Funq是否支持ResolveAll?

时间:2012-01-12 09:21:32

标签: c# .net dependency-injection ioc-container funq

Funq IoC容器是否支持解析某类型的所有注册?像这两个中的任何一个:

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

2 个答案:

答案 0 :(得分:8)

Funq没有ResolveAll方法,但您只需注册一个IEnumerable<IFoo>,然后在问题中显示Resolve<IEnumerable<IFoo>>()即可解决此问题。

但一般情况下,最好为容器请求集合,而是使用composites。这样,您可以简单地将IFoo作为依赖项注入,而不是强制该依赖项的使用者迭代列表。相反,您嵌入了在复合内部循环IFoo实例列表的代码。这样可以保留代码DRY,并且不会强制您浏览分散在整个应用程序中的(可能的)数十个foreach (var foo in foos)语句,而必须按照项目的迭代方式进行更改。或者让我以另一种方式说:消费者有责任知道如何迭代所有IFoo

以下是IFoo复合材料的示例:

// 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.ToArray();
    }

    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元素的列表。 / p>

<强>更新

使用此复合模式,您可以轻松控制每个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.ToArray();
    }

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

            foo.FooThatThing(bar);
        }
    }
}

现在我们可以在构造函数中注入一些lambdas,而不是注入一些IFoo

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

这将确保每次调用CompositeFoo FooThatThing时,都会查询容器以查找新的IFoo个实例。这允许同一个消费者多次调用FooThatThing,甚至允许CompositeFoo注册为单身。

此建议通常适用于所有容器和依赖注入,并不特定于Funq的使用。

答案 1 :(得分:0)

对于那些想要反模式的人,仅仅因为你可能不想实现工厂模式,这里是我的版本基于最后的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);
            }
        }
    }  
}