Reputation: 351
Consider the scenario where a service's constructor has a mix of parameters: some you want the container to resolve based on whatever you registered, and some you want the consumer of the service to provide.
With Autofac, one may add a Factory delegate to a service as follows (examples below are taken from their documentation) :
public class Shareholding
{
// We don't have to add the quote service to the factory delegate.
public delegate Shareholding Factory(string symbol, uint holding);
// Parameters in the constructor that don't show up in
// the delegate will come from the appropriate lifetime scope.
public Shareholding(string symbol, uint holding, IQuoteService quoteService)
{
...
}
You register the service "normally":
builder.RegisterType<Shareholding>();
Autofac does some magic, and now the Factory is requestable on consumers. Autofac will supply the missing parameters (IQuoteService in this case) with whatever is registered, if possible, and provide a new instance each time the factory is invoked, e.g.:
public class Portfolio
{
private readonly Shareholding.Factory _shareHoldingFactory;
private readonly List<Shareholding> _holdings = new List<Shareholding>();
public Portfolio(Shareholding.Factory shareholdingFactory)
{
_shareHoldingFactory = shareholdingFactory;
}
public void Add(string symbol, uint holding)
{
_holdings.Add(_shareHoldingFactory(symbol, holding));
}
}
Does Simple Injector have something similar to this? I have gone around in circles considering and trying a few things:
Func<string, uint, QuoteService>
won't work because then I'll need to request Func<string, uint, QuoteService>
on my consumers instead of just QuoteService.Factory, which will be annoying if it's used in more than one place and the parameters are changed.Register<TService>(Func<TService> instanceCreator)
overload won't work because the Func doesn't take parameters and there isn't an overload for one that does (right?)I feel like I'm missing something obvious because this is such a useful feature.
Upvotes: 1
Views: 277
Reputation: 172606
Does Simple Injector have something similar to this?
No, it doesn't contain such feature out-of-the-box. It does so because Simple Injector discourages a design were constructors mix runtime data with Volatile Dependencies. You can find the reasoning here.
To take your Shareholding
as an example, depending on the role of this class, you can:
symbol
and holding
from its constructor and pass it through using one of Shareholding
's public methods. This is especially useful when Shareholding
is an application component; i.e. a class that mainly contains behavior.IQuoteService
Volatile Dependency from Shareholding
's constructor and pass it through one of its public methods. In other words, use Method Injection rather than Constructor Injection. This is especially useful when Shareholding
itself is a Domain Entity, where symbol
and holding
are properties of the entity, and where there is a Domain Method that requires the IQuoteService
Volatile Dependency to function.Although Simple Injector does not contain this feature out-of-the-box -and I would generally advise against it- it is certainly possible to add this feature on top of Simple Injector. There are examples in Simple Injector's Code Samples project that demonstrate ways to achieve this.
And of course you can register the delegates by hand, which is not that much work if you have just a few of these types of registrations. For instance:
container.RegisterInstance<Shareholding.Factory>((symbol, holding) =>
new Shareholding(symbol, holding, container.GetInstance<IQuoteService>()));
Upvotes: 2