60

I have been helping a few friends on a project and there is a class that uses Ninject. I am fairly new to C# and I have no idea what that class is doing, which is why I need to understand Ninject. Can anyone explain what Ninject is and when does one use it(with example if possible)? Or if you can point to some links that would be great too.

I tried this question: Ninject tutorials/documentations? but it didn't really help a beginner like me.

Community
  • 1
  • 1
bachkoi32
  • 1,426
  • 4
  • 20
  • 31
  • 6
    ninject is a dependency injection framework. start here: https://github.com/ninject/ninject/wiki/Dependency-Injection-By-Hand for a good introduction to the concept and how to use ninject – drch Jun 28 '13 at 23:52
  • The documentation in the wiki explains it all from the basics. http://www.ninject.org/wiki.html – spender Jun 29 '13 at 00:04

4 Answers4

49

Ninject is dependency injector for .NET, practical realisation of pattern Dependency Injection (form of Inversion of Control pattern).

Suppose you have two classes DbRepository and Controller:

class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

class DbRepository {
   // ... some bussiness logic here ...
}

So, now you have two problems:

  1. You must initialize _repository to use it. You have several options for doing this:

    1. Manually, within the constructor. But what if the constructor of DbRepository changes? You would need to rewrite your Controller class because code it's dependent upon was changed. It's not hard if you have only one Controller, but if you have a couple of classes that have a dependency on your Repository you have a real problem.
    2. You can use a service locator or factory. But now you have a dependency on your service locator. You have a global service locator and all code must use it. How you will you change the behavior of your service locator when you need to use it in one part of your code for activation logic but for something else in another part of your code? There is only one way - passing the service locator through constructors. But with more and more classes you will need to pass it more and more times. Anyway, it's a good thought but in the long run, it's a bad idea.

      class Controller {
         private DbRepository _repository;
      
         public Controller() {
           _repository = GlobalServiceLocator.Get<DbRepository>()
         }
      
         // ... some methods that uses _repository
      }
      
    3. You can use dependency injection. Look at the code:

      class Controller {
         private IRepository _repository;
      
         public Controller(IRepository repository) {
            _repository = repository;
         }
      }
      

      Now when you need your controller you write: ninjectDevKernel.Get<Controller>(); or ninjectTestKernel.Get<Controller>();. You can switch beetween dependency resolvers as fast as you want. See? It's simple, you don't need to write a lot.

  2. You can't create unit tests for it. Your Controller has a dependency on DbRepository and if you want to test some method that uses repository, your code will go to the database and ask it for data. That's slow, very slow. If your code in DbRepository changes, your unit test on Controller will fall. Only integration test must warn you of 'problems' in this case. What you need in unit tests - is to isolate your classes and test only one class in one test (in ideal - only one method). If your DbRepository code fails, you will think that Controller code failed - and that's bad (even if you have tests for DbRepository and Controller - they both will fail and you can start from the wrong place). It takes a lot of time to determine where the error really is. You need to know that class A is ok, and it was class B where something failed.

  3. When you want to replace DbRepository with something else in all your classes, you have to do a lot of work.

  4. You can't easily control the lifetime of DbRepository. An object of this class is created on initialization of Controller and deleted when Controller is deleted. There is no sharing between different instances of the Controller class and there is no sharing between other classes. With Ninject you can simply write:

    kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
    

A special feature of dependency injection - agile development! You describe that your controller uses a repository with interface IRepository. You don't need to write DbRepository, you can simply create a MemoryRepository class and develop Controller while another person develops DbRepository. When work on DbRepository is finished, you just rebind in your dependency resolver that default IRepository is now DbRepository. Have a lot of controllers? All of them will now use DbRepository. That's cool.

Read more:

  1. Inversion of control (wiki)
  2. Dependency injection (wiki)
  3. Inversion of Control Containers and the Dependency Injection pattern (Martin Fowler)
bwegs
  • 3,769
  • 2
  • 30
  • 33
Viktor Lova
  • 4,776
  • 2
  • 19
  • 25
  • 4
    I still don't understand how ninject would make it easier than just injecting manually. If I have an upload file strategy for every company and the strategy needs to be loaded at run time based on the company selected. It seems like ninject doesn't change much of the code whether I do it manually or use ninject. The only benefits I see from ninject is if I want to write to database or log to a text file during testing then I can easily change the strategy on start up. – MIKE Jan 29 '16 at 17:14
  • 1
    Even for that, You can use an abstract factory to achieve the same. Where the facory can call method like LoadAssemblyAndUnwrap, or GetType to create the an instance of the correct type. The assembly or type to use maybe stored in a config file. Thus no need to depend on another library, Ninject, Unity, or things like that. :) – gWay Jun 09 '17 at 17:28
  • 1
    Ninject and another DI containers are useful in two cases: 1) Too much dependencies to tie all together. Declarative config is more readable & writable 2) When you don't know resolving type in compile-time (for example, Asp controllers are instantiated at runtime and you really can have a lot of then – Viktor Lova Jun 14 '17 at 23:02
  • Could the part about the service locator, in point 2 of the first problem, please be rephrased? Also it looks like point 3 under the first problem relies on some of the context of point 2 to be understood completely, but point 2 isn't completely clear. – Panzercrisis Dec 04 '17 at 19:47
41

Ninject is an Inversion of Control container.

What does it do?

Suppose you have a Car class that depends on a Driver class.

public class Car 
{
   public Car(IDriver driver)
   {
      ///
   }
}

In order to use the Car class you build it like so:

IDriver driver = new Driver();
var car = new Car(driver);

A IoC containter centralizes the knowledge about how to build classes. It is a central repository that knows a few things. For example, it knows that the concrete class that you need to use to build a car is a Driver and not any other IDriver.

For example, if you are developing a MVC application, you can tell Ninject how to build your controllers. You do so by registering which concrete classes satisfy specific interfaces. At run time Ninject will figure out which classes are needed to build the required controller, and all behind the scenes.

// Syntax for binding
Bind<IDriver>().To<Driver>();

This is beneficial because it lets you build systems that are more easily unit testable. Suppose that Driver encapsulates all database access for Car. In a unit test for Car you can do this:

IDriver driver = new TestDriver(); // a fake driver that does not go to the db
var car = new Car(driver);

There are entire frameworks that take care of automatically creating testing classes for you and they are called mocking frameworks.

For more information:

Sklivvz
  • 30,601
  • 24
  • 116
  • 172
  • Why would you want a test class? If you have a development environment, why would you not want to go to the database? I have worked on projects using ninject and can't see the benefit. We never change the concrete class, it's always the same. – Kelly Jan 02 '18 at 19:00
  • Well, some people prefer to test classes in isolation from the DB. This tool gives them the opportunity to do so. If a test DB works in your setup, that's also fine. – Sklivvz Jul 30 '18 at 07:26
  • @Kelly It removes the requirement for that test DB which a) removes a dependency to running the tests, and b) You're talking more about integration tests than unit tests. Unit tests don't _care_ what is going on in DB, they don't even care that the whole feature works, all they care about is "Does this *specific piece of code* work". – Matt Brewerton Jan 21 '19 at 14:18
8

Other answers are great but I would also like to point out this Implementing Dependency Injection using Ninject article.
This is one of the best articles I ever read which explains Dependency Injection and Ninject with a very elegant example.

Here's the snippet from the article:

Below Interface will be implemented by our (SMSService) and (MockSMSService), basically the new Interface (ISMSService) will expose the same behaviors of both services as the code below:

public interface ISMSService
 {
 void SendSMS(string phoneNumber, string body);
 }

(SMSService) implementation to implement the (ISMSService) interface:

public class SMSService : ISMSService
 {
 public void SendSMS(string mobileNumber, string body)
 {
 SendSMSUsingGateway(mobileNumber, body);
 }




private void SendSMSUsingGateway(string mobileNumber, string body)
 {
 /*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

(MockSMSService) with totally different implementation using the same interface:

public class MockSMSService :ISMSService
 {
 public void SendSMS(string phoneNumber, string body)
 {
 SaveSMSToFile(phoneNumber,body);
 }

private void SaveSMSToFile(string mobileNumber, string body)
 {
 /*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

we need to implement a change to our (UIHandler) class constructor to pass the dependency through it, by doing this, the code which uses the (UIHandler) can determine which concrete implementation of (ISMSService) to use:

public class UIHandler
 {
 private readonly ISMSService _SMSService;

public UIHandler(ISMSService SMSService)
 {
 _SMSService = SMSService;
 }
 public void SendConfirmationMsg(string mobileNumber) {

 _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
 }
 }

Now, we have to create a separate class (NinjectBindings) which inherits from (NinjectModule). This class will be responsible to resolve dependencies at run time, then we’ll override the load event which is used to configure the binding in it. The nice thing about Ninject is that we do not need to change our code in (ISMSService), (SMSService), and (MockSMSService).

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
 Bind<ISMSService>().To<MockSMSService>();
 }
 }

Now in UI form code, we’ll use the binding for Ninject which will determine which implementation to use:

class Program
 {
 static void Main(string[] args)
 {
 IKernel _Kernal = new StandardKernel();
 _Kernal.Load(Assembly.GetExecutingAssembly());
 ISMSService _SMSService = _Kernal.Get<ISMSService>();

UIHandler _UIHandler = new UIHandler(_SMSService);
 _UIHandler.SendConfirmationMsg("96279544480");

Console.ReadLine();
 }
 }

Now the code is using the Ninject Kernal to resolve all chain of dependencies, if we want to use the real service (SMSService) in Release mode (on production environment) instead of the mock one, we need to change on the Ninject binding class (NinjectBindings) only to use the right implementation or by using the #if DEBUG directive as below:

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
#if DEBUG
 Bind<ISMSService>().To<MockSMSService>();
#else
 Bind<ISMSService>().To<SMSService>();
#endif

}
 }

Now our binding class (NinjectBindings) is living on the top of all our execution code and we can control the configuration easily in once place.


Also, see What is Inversion of Control? some very simple examples are mentioned to understand IoC.

Community
  • 1
  • 1
GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
0

You have to understand the Dependency Injection(DI) first. Notice here,

public interface IService
{
    void Serve();
}
public class Service1 : IService
{
    public void Serve() {
        Console.WriteLine("Service1 Called");
    }
}
public class Service2 : IService
{
    public void Serve() {
        Console.WriteLine("Service2 Called");
    }
}
public class Service3 : IService
{
    public void Serve() {
        Console.WriteLine("Service3 Called");
    }
}
public class Client
{
    private IService service;

    public Client(IService _service)   //Constructor injection
    {
        service = _service;
    }
    public void ServeMethod() {
        service.Serve();  //Notice here, this Serve() method has no idea what to do.
    }                     // runtime will assign the object, that is Ninject
}


class Program
{
    static void Main(string[] args)
    {
        IService s1 = new Service1();  //N.B. Ninject assigns object with interface 

        Client c1 = new Client(s1);    

        c1.ServeMethod();         

        IService s2 = new Service2();  //N.B. Ninject assigns object with interface

        c1 = new Client(s2);

        c1.ServeMethod();

        IService s3 = new Service3(); //N.B. Ninject assigns object with interface

        c1 = new Client(s3);

        c1.ServeMethod();


        Console.ReadKey();
    }
}       
  // Ninject creates object in runtime for interface in runtime in ASP.NET MVC project.

/* Output:
Service1 Called
Service2 Called
Service3 Called */

Md Shahriar
  • 2,072
  • 22
  • 11