Does Simple Injector have a feature like Autofac Factory Delegates?

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:

  1. Registering an, e.g., 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.
  2. I don't really want to create factory wrappers for all my services. Having been spoilt by Autofac, this just seems like too much overhead now.
  3. The 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

Answers (1)

Steven
Steven

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:

  1. Remove 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.
  2. Remove the 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

Related Questions