You typically won’t need to resolve dependencies manually, as Injexit will do it automatically for you behind the scenes. It is considered a best practice to only expose the container to bootstrapping code and abstract it completely away from the rest of your application. If some of your classes need to create or resolve objects on-the-fly, use a factory classes or factory lambdas to hide the container from them (see Factories section).
Because the IContainer
also implements IResolver
, all the following examples would also work directly with the container. It is just a best practice to work with the most specific interface (for instance, IResolver
).
Resolving a type known at compile-time
resolver.Resolve<IFoo>();
Resolving a type only known at run-time
Container.Resolve(type);
Overriding bindings at resolution-time
When you want to specify bindings that should only apply to the current resolution operation (or that should temporarily override existing bindings), you can create a sub-container on the fly with the Using()
extension method, just before calling Resolve()
:
resolver
.Using(x =>
{
x.Bind<IFoo, Foo2>())
x.Bind<IGoo, Goo2>())
})
.Resolve<IBar>();
When you need to override bindings with specifics instances…
resolver
.Using(x =>
{
x.BindInstance(foo))
x.BindInstance(goo))
})
.Resolve<IBar>();
…there is an equivalent shorthand syntax that supports from one to three instances:
resolver.UsingInstance(foo).Resolve<IBar>();
resolver.UsingInstances(foo, goo).Resolve<IBar>();
resolver.UsingInstances(foo, goo, hoo).Resolve<IBar>();
We will see this syntax in action in the Factories section below.
Factories
When a class needs to create or resolve objects on-the-fly, you can use factory classes or lambdas. Here we will only look at the simplest approach, using factory lambda functions. Let’s consider the following class, that gets injected a Func<IFoo>
factory lambda function and uses it at run-time to create instances of IFoo
on-the-fly:
public class Bar : IBar
{
private readonly Func<IFoo> _fooFactory
public Bar(Func<IFoo> fooFactory)
{
_fooFactory = fooFactory;
}
// Some method that will be called at run-time that needs to create IFoo instances on-the-fly
public void SomeMethod()
{
var foo = _fooFactory();
}
}
Now, let’s configure that factory lambda and bind it:
Container.Bind<IFoo, Foo>(); // This is still needed, as the factory needs to resolve IFoo
Container.BindInstance<Func<IFoo>>(
() => Container.Resolve<IFoo>());
Note that what we are binding here is an instance of the lambda, not an instance of IFoo.
Because this simple type of factory with no parameters is quite common, there is a shorthand syntax, equivalent to the above binding:
Container.BindDefaultFactory<IFoo>();
Parametrized factories
In the previous example, because we are using Container.Resolve<IFoo>()
to resolve our object, any dependency that it might have will also be resolved automatically for you. That’s why you should avoid using the new
keyword in your factories. But what about if you need to pass a specific parameter at run-time? Let’s say our Foo
class needs to know its parent IBar
that created it, and maybe some other IGoo
object:
public class Foo : IFoo
{
public Foo(IBar parentBar, IGoo someGoo)
{
// ...
}
}
Then we can add an IBar
parameter to the factory lambda and temporarily add that object to the resolver with .Using()
just before calling .Resolve()
:
Container.Bind<IFoo, Foo>();
Container.Bind<IGoo, Goo>();
Container.Bind<IBar, Bar>();
Container.BindInstance<Func<IBar, IFoo>>(
(IBar parentBar) => Container
.UsingInstance(parentBar)
.Resolve<IFoo>());
Notice that IGoo
will be resolved and injected automatically behind the scene. In this particular scenario, we only want to pass IBar
explicitly to the factory. So, let’s call our factory with the IBar
parameter (in this case, this
is the parent IBar
we want to inject):
public class Bar : IBar
{
private readonly Func<IBar, IFoo> _fooFactory
public Bar(Func<IBar, IFoo> fooFactory)
{
_fooFactory = fooFactory;
}
public void SomeMethod()
{
var foo = _fooFactory(this);
}
}
There is a shorthand syntax for default factories with zero up to three parameters:
Container.BindDefaultFactory<IFoo>(); // Func<IFoo>
Container.BindDefaultFactory<IBar, IFoo>(); // Func<IBar1, IFoo>
Container.BindDefaultFactory<IBar1, IBar2, IFoo>(); // Func<IBar1, IBar2, IFoo>
Container.BindDefaultFactory<IBar1, IBar2, IBar3, IFoo>(); // Func<IBar1, IBar2, IBar3, IFoo>
Typed factories
Container.BindTypedFactory<IFoo>(); // Func<Type, IFoo>
Container.BindTypedFactory<IBar, IFoo>(); // Func<Type, IBar, IFoo>
Container.BindTypedFactory<IBar1, IBar2, IFoo>(); // Func<Type, IBar1, IBar2, IFoo>
Container.BindTypedFactory<IBar1, IBar2, IBar3, IFoo>(); // Func<Type, IBar1, IBar2, IBar3, IFoo>