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>();
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>();
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 IFoo
s.
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 IFoo
s 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.
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 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);
}
}
}
}
© 2022 - 2024 — McMap. All rights reserved.