Reputation: 33581
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
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
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