benstpierre
benstpierre

Reputation: 33581

Is there an equivalent of Guice Providers in Simple Injector?

Is there an equivalent injecting Guice Providers in Simple Injector?

I need to inject a dependency into a constructor that will let me create as many instances of a dependency as needed. In guice it would look like this...

public class RealBillingService implements BillingService {
  private final Provider<CreditCardProcessor> processorProvider;
  private final Provider<TransactionLog> transactionLogProvider;

  @Inject
  public RealBillingService(Provider<CreditCardProcessor> processorProvider,
      Provider<TransactionLog> transactionLogProvider) {
    this.processorProvider = processorProvider;
    this.transactionLogProvider = transactionLogProvider;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    // each call to get creates a new instance in Guice as per scope configs
    CreditCardProcessor processor = processorProvider.get();  
    TransactionLog transactionLog = transactionLogProvider.get();

    /* use the processor and transaction log here */
  }
}

So perhaps a C# equivalent could be this inside SimpleInjector?

    private readonly MailSender _mailSenderProvider;

    public MailService(Func<MailSender> mailSenderProvider)
    {
        _mailSenderProvider = mailSenderProvider;
    }

    public void SendMail()
    {
        var mailSender = _mailSenderProvider.Invoke();
        mailSender.SendSomeMail("Hello world");
    }

I tried injecting Func in my real code and got this...

{"No registration for type BootStrapper could be found and an implicit registration could not be made. The constructor of type BootStrapper contains the parameter of type Func with name 'storeType' that is not registered. Please ensure Func is registered in the container, or change the constructor of BootStrapper."}

Upvotes: 0

Views: 637

Answers (2)

benstpierre
benstpierre

Reputation: 33581

I found the following example in the SimpleInjector docs

http://simpleinjector.readthedocs.org/en/latest/howto.html#register-factory-delegates

   public static void AllowResolvingFuncFactories(this ContainerOptions options)
    {
        options.Container.ResolveUnregisteredType += (s, e) =>
        {
            var type = e.UnregisteredServiceType;

            if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(Func<>))
            {
                return;
            }

            Type serviceType = type.GetGenericArguments().First();

            InstanceProducer registration = options.Container.GetRegistration(serviceType, true);

            Type funcType = typeof(Func<>).MakeGenericType(serviceType);

            var factoryDelegate = Expression.Lambda(funcType,
                registration.BuildExpression()).Compile();

            e.Register(Expression.Constant(factoryDelegate));
        };
    }

Then on my container I call this...

        // Allow types of Func<T> to be resolved
        container.Options.AllowResolvingFuncFactories();

        // 3. Optionally verify the container's configuration.
        container.Verify();

Now I can inject Func< MyClass > and when I invoke the Func it returns as many instances as I want of that type.

All thanks to C# reified types and Simpleinjector's awesome api!

Upvotes: 1

qujck
qujck

Reputation: 14580

You need to explicitly configure factory delegates with Simple Injector (see here)

var container = new Container();
container.Register<MailSender>();
container.RegisterSingle<Func<MailSender>>(() => container.GetInstance<MailSender>());

You may want to consider separating concerns by adding a new abstraction. If you define an IMailSender you can then create a MailSenderProxy that is responsible for ensuring a new MailSender instance for each message.

public interface IMailSender {
    void Send(string message);
}

public class MailSender : IMailSender {
    public void Send(string message) {
    }
}

public class MailSenderProxy : IMailSender {
    private readonly Func<IMailSender> mailSenderFactory;

    public MailSenderProxy(Func<IMailSender> mailSenderFactory) {
        this.mailSenderFactory = mailSenderFactory;
    }

    public void Send(string message) {
        this.mailSenderFactory().Send(message);
    }
}

This abstracts away the requirement of creating a new MailSender for each mail (this is possibly not something the MailService should know about)

public class MailService {
    private readonly IMailSender sender;

    public MailService(MailSender sender) {
        this.sender = sender;
    }

    public void SendMail() {
        this.sender.Send("Message");
    }
} 

The Container configuration would look something like this

var container = new Container();
container.Register<MailSender>();
container.RegisterSingle<Func<IMailSender>>(() =>
    container.GetInstance<MailSender>());
container.Register<IMailSender, MailSenderProxy>();
container.Verify();

Upvotes: 1

Related Questions