Does Funq support ResolveAll?
Asked Answered
G

2

11

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>();
Glasshouse answered 12/1, 2012 at 9:21 Comment(0)
S
9

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.

Slang answered 12/1, 2012 at 10:27 Comment(4)
Thanks for the answer about Funq, Steven. Your use of composition is a good work around, but I don't agree with using it generally for any container. What about when I need to control the life cycle of each registrant differently? The consumer is not forced to iterate the list - they can always resolve a single instance when needed - it is their choice. Also, Common Service Locator commonservicelocator.codeplex.com explicitly implements multiple resolution in GetAllInstances().Glasshouse
@Sean: The fact the Common Service Locator contains a GetAllInstances, doesn't directly make it a good practice to use it (Service Locator is by itself an anti-pattern). In fact, the Simple Injector (a container I develop) contains a GetAllInstances method, but in I advice against using it in most cases (however, it is useful within the Composition Root). Furthermore, when you need to control the lifetime of each element in the composite, it is a matter of changing the registration. See my update.Slang
What if I'm building a pipeline of request and response processors and I have 30-40 of each? Is there no way to just say give me all implementations of IProcessor without registering every single one individually?Karakoram
Jack, it's time to start using a real DI Container.Slang
P
0

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);
            }
        }
    }  
}
Pelaga answered 22/6, 2016 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.