Question

Does the Funq IoC container support resolving all registrations for a type? Something like either of these:

IEnumerable<IFoo> foos = container.Resolve<IEnumerable<IFoo>>();
IEnumerable<IFoo> foos = container.ResolveAll<IFoo>();
Was it helpful?

Solution

Funq does not have a ResolveAll method, but you can simply register an IEnumerable<IFoo> and resolve it with Resolve<IEnumerable<IFoo>>() as you show in your question.

In general however, it is better not to request the container for collections, but use composites instead. This way you can simply inject an IFoo as a dependency, instead of forcing consumers of that dependency to iterate the list. Instead you embed the code that loops the list of IFoo instances inside the composite. This keeps your code DRY and doesn't force you to go through the (possible) dozens of foreach (var foo in foos) statements scattered throughout the application, when a change has to be made in the way the items are iterated. Or let me put it in an other way: it is not the responsibility of a consumer to know how to iterate all IFoos.

Here is an example of an 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);
        }
    }
}

Instead of registering an IEnumerable<IFoo>, you can register a CompositeFoo as IFoo:

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

Now you can let the container inject that CompositeFoo in consumers that take an IFoo argument, which makes them unaware that they are in fact dealing with a list of IFoo elements.

UPDATE:

Using this composite pattern, you can easily control the lifetime of each IFoo item. It's just a matter of calling back into the container. With Funq,it would look like this:

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

This way you can register Foo1 as singleton and Foo2 as transient for instance. When the CompositeFoo is reused however, Foo2 will not really be transient, but it's just a matter of changing the CompositeFoo and its registration to solve this problem. For instance, you can change your CompositeFoo to the following:

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);
        }
    }
}

Now instead of injecting some IFoos into the constructor, we can inject some lambdas in it:

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

This will ensure that every time CompositeFoo's FooThatThing is called, the container is queried for new IFoo instances. This allows FooThatThing to be called multiple times by the same consumer, and even allows CompositeFoo to be registered as singleton.

This advice holds for all containers and Dependency Injection in general, and is not specific to the use of Funq.

OTHER TIPS

For those of you who want the anti-pattern, simply because you might not want to implement a factory pattern, here is my version based on last 3.9.71.

Only downside is that it adds a bit more memory and you have to register the services through registerOneOfMany(). you might want to change the bag for a list with a lock (for faster reads) and switch to servicstack funq's default: 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);
            }
        }
    }  
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top