Ninject provider as factory method
Ninject is a very nice and easy to use, open source dependency injection framework. It is very popular within ASP.net MVC developers community and de-facto framework of choice for MVC applications.
I was implementing small feature recently. As user registered on site, he receives confirmation email and registration details. Pretty common functionality along the sites. So, I add next application service INotificationService
that took responsibility of sending email message to user. Nothing special, nothing complex.
namespace Trackyt.Core.Services
{
public class NotificationService : INotificationService
{
private IEMailService _emailService;
public NotificationService(IEMailService emailService)
{
_emailService = emailService;
}
public void NotifyUserOnRegistration(string usersEmail, string password)
{
var emailMessage = CreateEmailMessageForUser(usersEmail, password);
_emailService.SendEmail(emailMessage, "support");
}
//...
As I’ve tested and integrated it to application, everything were just fine. Till the time I reset database and re-run tests. The problem, that INotificationService
itself depends on IEmailService
that uses ICredentialsRepository
to extract email server credentials (account, password, settings) from database. After database is reset, Credentials
table is just empty and IEmailService
throws exception that there are no credentials, so send email is impossible. I could not add credentials as SQL to database script, since it depend on configuration and exposes private password. Do it manually after each reset of database is boring task. Furthermore, I don’t want my application to send any emails as I just do some development testing.
The obvious design workaround is - define INotificationServiceFactory
that responsible for NotificationService
instantiation. Factory decides, if application run in debug
mode, just stub of NotificationService
is used, otherwise real implementation is used.
namespace Trackyt.Core.Services
{
public class NotificationServiceFactory : INotificationServiceFactory
{
public INotificationService GetService()
{
if (HttpContext.Current.IsDebuggingEnabled)
{
// just stub..
return new NotificationServiceForDebug();
}
// here I need to pass EmailService to constructor
return new NotificationService ( // ??? );
}
// ...
But it is not so easy as it seems to.. Here the problem: NotificationService
have to accept EmailService
, that have to be created created by DI framework (I could not create it by new
since I loose all benefits of inversion of control). So, in factory I need to have a IKernel
object - Ninject core object, for instantiating of objects from Inversion of Control container. It should be extended with constructor taking IKernel
as argument.
Issues:
- Circular dependency - factory is defined in Core assembly, kernel is defined in Web application.. Web application references Core, to make it work now Core need to reference Web (it is actually possible, but very ugly.. I try to avoid such things).
- Additional references - now Core also need to reference Ninject, to make it compliable.
- Violation of Dependency inversion principle - one of the SOLID principles of object oriented systems. Model must not depend on infrastructure.
Fortunately Ninject provides functionality to avoid issues mentioned above! Instead of binding to exact type, like
Bind<INotificationServiceFactory>().To<NotificationServiceFactory>();
I can bind creation of type to Provider:
Bind<INotificationService>().ToProvider<TrackyNotificationServiceProvider>();
Provider is class that implement IProvider interface, which is actually just one method CreateInstance
. CreateInstance, receives IContext
object as parameter that contains IKernel
. TrackyNotificationServiceProvider
is placed on same level as the rest of Ninject infrastructure code is placed. Model remains clear and exact and do not mess up with infrastructure code.
namespace Web.Infrastructure
{
public class TrackyNotificationServiceProvider : Provider<INotificationService>
{
protected override INotificationService CreateInstance(IContext context)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
return new NotificationServiceForDebug();
}
return new NotificationService(context.Kernel.Get<IEMailService>());
}
}
}
Now, in case of INotificationService
object need to be instantiated (in my case it is injected to RegistrationController
as constructor parameter), CreateInstance
is called. If web.config contains <compilation debug="true" targetFramework="4.0">
the stub of service is created. On production, where <compilation debug="false" targetFramework="4.0">
, real instance of NotificationService is put to work.
Ninject is a very nice and easy to use, open source dependency injection framework. It is very popular within ASP.net MVC developers community and de-facto framework of choice for MVC applications.
I was implementing small feature recently. As user registered on site, he receives confirmation email and registration details. Pretty common functionality along the sites. So, I add next application service INotificationService
that took responsibility of sending email message to user. Nothing special, nothing complex.
namespace Trackyt.Core.Services { public class NotificationService : INotificationService { private IEMailService _emailService; public NotificationService(IEMailService emailService) { _emailService = emailService; } public void NotifyUserOnRegistration(string usersEmail, string password) { var emailMessage = CreateEmailMessageForUser(usersEmail, password); _emailService.SendEmail(emailMessage, "support"); } //...
As I’ve tested and integrated it to application, everything were just fine. Till the time I reset database and re-run tests. The problem, that INotificationService
itself depends on IEmailService
that uses ICredentialsRepository
to extract email server credentials (account, password, settings) from database. After database is reset, Credentials
table is just empty and IEmailService
throws exception that there are no credentials, so send email is impossible. I could not add credentials as SQL to database script, since it depend on configuration and exposes private password. Do it manually after each reset of database is boring task. Furthermore, I don’t want my application to send any emails as I just do some development testing.
The obvious design workaround is - define INotificationServiceFactory
that responsible for NotificationService
instantiation. Factory decides, if application run in debug
mode, just stub of NotificationService
is used, otherwise real implementation is used.
namespace Trackyt.Core.Services { public class NotificationServiceFactory : INotificationServiceFactory { public INotificationService GetService() { if (HttpContext.Current.IsDebuggingEnabled) { // just stub.. return new NotificationServiceForDebug(); } // here I need to pass EmailService to constructor return new NotificationService ( // ??? ); } // ...
But it is not so easy as it seems to.. Here the problem: NotificationService
have to accept EmailService
, that have to be created created by DI framework (I could not create it by new
since I loose all benefits of inversion of control). So, in factory I need to have a IKernel
object - Ninject core object, for instantiating of objects from Inversion of Control container. It should be extended with constructor taking IKernel
as argument.
Issues:
- Circular dependency - factory is defined in Core assembly, kernel is defined in Web application.. Web application references Core, to make it work now Core need to reference Web (it is actually possible, but very ugly.. I try to avoid such things).
- Additional references - now Core also need to reference Ninject, to make it compliable.
- Violation of Dependency inversion principle - one of the SOLID principles of object oriented systems. Model must not depend on infrastructure.
Fortunately Ninject provides functionality to avoid issues mentioned above! Instead of binding to exact type, like
Bind<INotificationServiceFactory>().To<NotificationServiceFactory>();
I can bind creation of type to Provider:
Bind<INotificationService>().ToProvider<TrackyNotificationServiceProvider>();
Provider is class that implement IProvider interface, which is actually just one method CreateInstance
. CreateInstance, receives IContext
object as parameter that contains IKernel
. TrackyNotificationServiceProvider
is placed on same level as the rest of Ninject infrastructure code is placed. Model remains clear and exact and do not mess up with infrastructure code.
namespace Web.Infrastructure { public class TrackyNotificationServiceProvider : Provider<INotificationService> { protected override INotificationService CreateInstance(IContext context) { if (HttpContext.Current.IsDebuggingEnabled) { return new NotificationServiceForDebug(); } return new NotificationService(context.Kernel.Get<IEMailService>()); } } }
Now, in case of INotificationService
object need to be instantiated (in my case it is injected to RegistrationController
as constructor parameter), CreateInstance
is called. If web.config contains <compilation debug="true" targetFramework="4.0">
the stub of service is created. On production, where <compilation debug="false" targetFramework="4.0">
, real instance of NotificationService is put to work.