Book Image

Mastering Ninject for Dependency Injection

By : Daniel Baharestani
Book Image

Mastering Ninject for Dependency Injection

By: Daniel Baharestani

Overview of this book

Dependency injection is an approach to creating loosely coupled applications. Maintainability, testability, and extensibility are just a few advantages of loose coupling. Ninject is a software library which automates almost everything that we need in order to implement a dependency injection pattern. Mastering Ninject for Dependency Injection will teach you everything you need to know in order to implement dependency injection using Ninject in a real-life project. Not only does it teach you about Ninject core framework features that are essential for implementing dependency injection, but it also explores the power of Ninject's most useful extensions and demonstrates how to apply them. Mastering Ninject for Dependency Injection starts by introducing you to dependency injection and what it's meant for with the help of sufficient examples. Eventually, you'll learn how to integrate Ninject into your practical project and how to use its basic features. Also, you will go through scenarios wherein advanced features of Ninject, such as Multi-binding, Contextual binding, providers, factories and so on, come into play. As you progress, Mastering Ninject for Dependency Injection will show you how to create a multilayer application that demonstrates the use of Ninject on different application types such as MVC, WPF, WCF, and so on. Finally, you will learn the benefits of using the powerful extensions of Ninject.
Table of Contents (12 chapters)

My First DI Application


We start our example with a service class in which the concerns are not separated. Then we will improve maintainability step-by-step, by first separating concerns and then programming to interface in order to make our modules loosely coupled. At the final point, we will have our first DI application. The source code for all the examples of this book is available for download on the publisher's website.

The main responsibility of this service is to send an e-mail using the information provided. In order to make the example simple and clear, client initialization is omitted.

class MailService
{
  public void SendEmail(string address, string subject, string body)
  {
    var mail = new MailMessage();
    mail.To.Add(address);
    mail.Subject = subject;
    mail.Body = body;
    var client = new SmtpClient();
    // Setup client with smtp server address and port here
    client.Send(mail);
  }
} 

Then, we add some logging to it, so that we know what is going on in our service:

class MailService
{
  public void SendMail(string address, string subject, string body)
  {
    Console.WriteLine("Creating mail message...");
    var mail = new MailMessage();
    mail.To.Add(address);
    mail.Subject = subject;
    mail.Body = body;
    var client = new SmtpClient();
    // Setup client with smtp server address and port here
    Console.WriteLine("Sending message...");
    client.Send(mail);
    Console.WriteLine("Message sent successfully.");
  }
}

After a little while, we find it useful to add time to our logs. In this example, sending the mail message and logging functionality are two different concerns which are addressed in a single class, and it is not possible to change the logging mechanism without touching the MailService class. Therefore, in order to add time to our logs, we have to change the MailService class. So, let's re-factor this class and separate the concern of logging from sending a mail prior to adding the time functionality:

class MailService
{
  private ConsoleLogger logger;
  public MailService()
  {
    logger = new ConsoleLogger();
  }

  public void SendMail(string address, string subject, string body)
  {
    logger.Log("Creating mail message...");
    var mail = new MailMessage();
    mail.To.Add(address);
    mail.Subject = subject;
    mail.Body = body;
    var client = new SmtpClient();
    // Setup client with smtp server address and port here
    logger.Log("Sending message...");
    client.Send(mail);
    logger.Log("Message sent successfully.");
  }
}

The ConsoleLogger class is only responsible for the logging mechanism, and this concern is removed from MailService. Now, it is possible to modify the logging mechanism without affecting MailService:

class ConsoleLogger
{
  public void Log(string message)
  {
    Console.WriteLine("{0}: {1}", DateTime.Now, message);
  }
}

Now, we need to write our logs in Windows Event Log rather than showing them in console. Looks like we need an EventLogger class as well:

class EventLogger
{
  public void Log(string message)
  {
    EventLog.WriteEntry("MailService", message);
  }
}

Although the concern of sending mail and logging are now separated in two different classes, MailService is still tightly coupled to the ConsoleLogger class, and it is not possible to replace its logger without modifying it. We are just one step away from breaking the tight coupling between the MailService and Logger classes. We should now introduce the dependencies as interfaces rather than concrete classes:

interface ILogger
{
  void Log(string message);
}

Both the ConsoleLogger and EventLogger classes should implement this interface:

class ConsoleLogger:ILogger
{
  public void Log(string message)
  {
    Console.WriteLine("{0}: {1}", DateTime.Now, message);
  }
}
class EventLogger:ILogger
{
  public void Log(string message)
  {
    EventLog.WriteEntry("MailService", message);
  }
}

Now, it is time to remove the references to the concrete ConsoleLogger class and address ILogger instead:

private ILogger logger;
public MailService()
{
  logger = new ILogger();
}

But the previous code won't compile because it doesn't make sense to instantiate an interface. We should introduce this dependency as a constructor parameter and have the concrete object injected into it by a third party:

public MailService(ILogger logger)
{
  this.logger = logger;
}

At this point, our classes are loosely coupled and we can change the loggers freely without affecting the MailService class. Using DI, we have also separated the concern of creating a new instance of the logger class, which includes the concern of deciding what concrete logger to use from the main responsibility of MailService, which is sending an e-mail:

internal class Program
{
  private static void Main(string[] args)
  {
    var mailService = new MailService(new EventLogger());
    mailService.SendMail("[email protected]", "My first DI App", "Hello World!");
  }
}

The main method of this application is where we decide what concrete objects to inject in our dependent classes. This (preferably) unique location in the application where modules are composed together is named Composition Root by Mark Seemann. For more information on DI, Dependency Injection in .NET, by Mark Seemann is recommended.