Alexander Beletsky's development blog

My profession is engineering

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.