[ASP.]04-Dependency Injection (DI) and Ninject

Source: Internet
Author: User
Tags log4net


[ASP.
MVC Calf Road]04-Dependency Injection (DI) and Ninject



This article directory:




Why dependency Injection is required



In the understanding MVC pattern article in the [ASP. Path] Series, we mention that an important feature of MVC is the separation of concerns (separation of concerns). We want the parts of the application to be as independent as possible, as little as possible from each other.

Our ideal scenario is that a component can not know or care about other components, but it can implement functional invocation of other components by providing a public interface. This situation is known as loose coupling.

Give a simple example. We want to customize a "premium" price calculator for the product linqvaluecalculator, which needs to implement the Ivaluecalculator interface. As shown in the following code:


public interface IValueCalculator {

    decimal ValueProducts(params Product[] products);

}

 

public class LinqValueCalculator : IValueCalculator {

    public decimal ValueProducts(params Product[] products) {

        return products.Sum(p => p.Price);

    }

}


The product class uses the same blog post as the first two articles. Now there is a shopping cart ShoppingCart class, which needs to have a function to calculate the total price of the goods in the shopping cart. But the shopping cart itself does not have the function of calculation, therefore, the shopping cart to embed a calculator component, this calculator component can be the Linqvaluecalculator component, but not necessarily is the Linqvaluecalculator component (later shopping cart upgrades, may be embedded in other more advanced calculators). So we can define the shopping cart ShoppingCart class:

1 public class ShoppingCart {

  2 // Calculate the total price of the goods in the shopping cart

  3 public decimal CalculateStockValue() {

  4 Product[] products = {

  5 new Product {Name = "Watermelon", Category = "Fruit", Price = 2.3M},

  6 new Product {Name = "Apple", Category = "Fruit", Price = 4.9M},

  7 new Product {Name = "Water Spinach", Category = "Vegetable", Price = 2.2M},

  8 new Product {Name = "Sweet", Category = "Vegetable", Price = 1.9M}

  9         };

10 IValueCalculator calculator = new LinqValueCalculator();

11

12 / / calculate the total price of goods

13 decimal totalValue = calculator.ValueProducts(products);

14

15 return totalValue;

16 }

17 }


The ShoppingCart class calculates the total price of a commodity through the Ivaluecalculator interface (rather than through Linqvaluecalculator). If you need to use a more advanced calculator for your shopping cart upgrade later, you only need to change the object behind new in line 10th (that is, replace the linqvaluecalculator), and no other code will change. This enables a certain degree of loose coupling. The relationship between the three is as follows:






This diagram illustrates that the ShoppingCart class relies on both the Ivaluecalculator interface and the Linqvaluecalculator class. So there is a problem, in the real world, if the calculator embedded in the shopping cart component is broken, will cause the whole shopping cart can not work properly, not to the whole shopping cart to replace! The best way is to completely separate the calculator component from the shopping cart so that no matter which component is broken, just change the corresponding component. The problem that we want to solve is to have the ShoppingCart component and the Linqvaluecalculator component completely disconnected, and the dependency injection design pattern is to solve this problem.



What is Dependency injection


The partial loose coupling implemented on the

is obviously not what we need. What we need is, inside a class, to get a reference to an object that implements the public interface without creating an instance of the object. This "need", referred to as DI (dependency injection, Dependency injection), and the so-called IOC (control inversion, inversion of controls) is a meaning. The

di is a design pattern that enables loose coupling through an interface. Beginners may be curious about why there are so many technical articles on the web that make up the pen for Di, because di is an important concept that developers must have, including MVC, to efficiently develop applications based on almost any framework. It is an important means of decoupling. The

di mode can be divided into two sections. One is to remove the dependency on the component (Linqvaluecalculator in the example above), and the second is to pass a reference to the component that implements the exposed interface through the class's constructor (or the setter accessor of the class). As shown in the following code:


public interface IValueCalculator {

    decimal ValueProducts(params Product[] products);

}

 

public class LinqValueCalculator : IValueCalculator {

    public decimal ValueProducts(params Product[] products) {

        return products.Sum(p => p.Price);

    }

}


In this way, we completely disconnect the dependency between ShoppingCart and Linqvaluecalculator. An instance reference to a class that implements the Ivaluecalculator interface (Linqvaluecalculator in the example) is passed as a parameter to the constructor of the ShoppingCart class. But the ShoppingCart class does not know or care about the class that implements the Ivaluecalculator interface, and is not responsible for manipulating the class. We can then describe the relationship between ShoppingCart, Linqvaluecalculator, and Ivaluecalculator:






When the program is running, the dependency is injected into the ShoppingCart, which is the use of the ShoppingCart constructor to pass an instance reference to the class that implements the Ivaluecalculator interface. Before the program runs (or at compile time), ShoppingCart and any classes that implement the Ivaluecalculator interface have no dependencies. (Note that the program runs with a specific dependency.) )

Note that the injection method used in the example above is called "construct injection", and we can also implement the injection through attributes, which is called "Setter injection", not for example, friends can look at the T2 phage article relies on injecting those things to learn more about Di.

Because di is often used in programming, some DI auxiliary tools (or Di containers) are present, such as unity and ninject. Because Ninject is lightweight and easy to use, plus I've only used Ninject, this series of articles chooses to use it to develop MVC applications. Let's start with the introduction of Ninject, but before that, let's introduce a plug-in-nuget to install Ninject.




Using the NuGet installation library



NuGet is a Visual Studio extension that simplifies the process of adding, updating, and deleting libraries (deployed as packages) in Visual Studio projects. For example, if you want to use the Log4net library in your project, if you do not have the nuget extension, you may want to search the web for log4net, extract the contents of the package to a specific location in the solution, add the assembly references in each project project, and finally use the correct settings to update Web. config. And NuGet can simplify all this. For example, in a project that relies on injection, to use a nuget library, you can right-click the project (or reference) and select Manage NuGet packages (VS2010 "Add
Library Package Reference "), such as:






Select "Online" in the pop-up window below, search for "Ninject", then do the appropriate actions:






In this article we just need to know how to use NuGet to install the library. The detailed use of NuGet can be viewed in the MSDN documentation: managing the project library with NuGet.




General steps for using Ninject



Before using Ninject, create a Ninject kernel object with the following code:


Class Program {

     Static void Main(string[] args) {

         / / Create a Ninject kernel instance

         IKernel ninjectKernel = new StandardKernel();

     }

} 


The use of Ninject kernel objects is generally divided into two steps. The first step is to bind an interface (Ivaluecalculator) to a class that implements the interface (Linqvaluecalculator), as follows:


...

/ / Bind the interface to the class that implements the interface

ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator<();

...


This binding operation is to tell Ninject that when an implementation of a request Ivaluecalculator interface is received, an instance of the Linqvaluecalculator class is returned.

The second step is to get the implementation of the Ivaluecalculator interface using the Ninject get method. In this step, Ninject will automatically create an instance of the Linqvaluecalculator class for us and return a reference to that instance. We can then inject this reference through the constructor into the ShoppingCart class. As shown in the following code:


...

/ / Get the object instance of the implementation interface

IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();

// Create a ShoppingCart instance and inject dependencies

ShoppingCart cart = new ShoppingCart(calcImpl);

// Calculate the total price of the item and output the result

Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

...


This is the general step in the use of ninject. The example correctly outputs the following results:






But it seems that the use of ninject seems to make the coding more cumbersome, friends will ask, it is not easier to use the following code directly:


...

IValueCalculator calcImpl = new LinqValueCalculator();

ShoppingCart cart = new ShoppingCart(calcImpl);

Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

...


Indeed, for a single simple Di, using ninject does seem troublesome. However, if you add multiple complex point dependencies, using Ninject can greatly improve the productivity of your coding.






Ninject How to improve coding efficiency



When we request Ninject to create an instance of a type, it examines the coupling between this type and other types. If there are dependencies, then Ninject will manage them based on dependency and create an instance of all the required classes. To explain this and to illustrate the convenience of using ninject encoding, we then create an interface Idiscounthelper and a class defaultdiscounthelper that implements the interface, with the following code:


/ / Discount calculation interface

Public interface IDiscountHelper {

     Decimal ApplyDiscount(decimal totalParam);

}

 

//Default discount calculator

Public class DefaultDiscountHelper : IDiscountHelper {

     Public decimal ApplyDiscount(decimal totalParam) {

         Return (totalParam - (1m / 10m * totalParam));

     }

}


The Idiscounhelper interface declares the ApplyDiscount method, Defaultdiscounterhelper implements the interface, and defines a ApplyDiscount method that hits 90 percent. Then we can add the Idiscounhelper interface as a dependency to the Linqvaluecalculator class. The code is as follows:


public class LinqValueCalculator : IValueCalculator { 

    private IDiscountHelper discounter; 

  

    public LinqValueCalculator(IDiscountHelper discountParam) { 

        discounter = discountParam; 

    } 

  

    public decimal ValueProducts(params Product[] products) { 

        return discounter.ApplyDiscount(products.Sum(p => p.Price)); 

    } 

}


The Linqvaluecalculator class adds a constructor to receive the implementation of the Idiscounthelper interface. The ApplyDiscount method of the interface is then called in the Valueproducts method to discount the calculated total price of the product and returns the total price of the discount.

Here, let's draw a diagram first. ShoppingCart, Linqvaluecalculator, Ivaluecalculator and the relationship between the newly added idiscounthelper and defaultdiscounterhelper:






In this way, we can also add more interfaces and implement the interface classes, interfaces and classes more and more, their diagrams will look like a dependency "chain", and the molecular structure in biology is similar.

According to the previous "two steps" using Ninject, we now write the code in the method in main that calculates the total price of the merchandise in the shopping cart, as follows:


1 class Program {

 2     static void Main(string[] args) {

 3         IKernel ninjectKernel = new StandardKernel();

 4 

 5         ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();

 6         ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();

 7 

 8         IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();

 9         ShoppingCart cart = new ShoppingCart(calcImpl);

10         Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

11         Console.ReadKey();

12     }

13 }


Output Result:






Code at a glance, although the newly added interface and a class, but the main method only adds a line 6th a code, get the implementation of the Ivaluecalculator interface object instance code does not need to make any changes.

Navigate to line 8th of the code, this line of code, what ninject do for us is:

When we need to use the implementation of the Ivaluecalculator interface (through the Get method), it creates an instance of the Linqvaluecalculator class for us. When you create an instance of the Linqvaluecalculator class, it checks to the class that it relies on the Idiscounthelper interface. It then creates an instance of the Defaultdiscounterhelper class that implements the interface, and injects the instance into the Linqvaluecalculator class through a constructor function. It then returns an instance of the Linqvaluecalculator class and assigns the object to the Ivaluecalculator interface (Calcimpl on line 8th).

In short, no matter how complex the dependency chain is, Ninject examines each interface and class that implements the interface in this way, and automatically creates an instance of the required class. The longer and more complex the dependency "chain" is, the more efficient it is to display the Ninject encoding.



How to bind Ninject



I personally divide the ninject into: general binding, specified value binding, self-binding, derived class binding, and conditional binding. This kind of classification is a bit far-fetched, just for the purpose of writing and convenient for readers to read, [b] is not the official classification. [/b]

1. General Bindings

In the previous example, binding an interface to the class that implements the interface with the bind and to methods, which is a generic binding. The example above is believed to have been mastered and is no longer a statement of exhaustion.

2, [b] Specify value binding [/b]

We know that through the Get method, Ninject will automatically help us create an instance of the class we need. However, some classes need to assign values to their properties when creating an instance, as we have modified the Defaultdiscounthelper class below:


public class DefaultDiscountHelper : IDiscountHelper { 

    public decimal DiscountSize { get; set; } 

  

    public decimal ApplyDiscount(decimal totalParam) { 

        return (totalParam - (DiscountSize / 10m * totalParam)); 

    } 

}


Added a Discountsize property to the Defaultdiscounthelper class, specifying a discount value (discountsize attribute value) when instantiating, otherwise the ApplyDiscount method has no meaning. And the instantiation of the action is Ninject automatic, how to tell Ninject in the instantiation of the class to assign a property to a specified value? At this point, we need to use the parameter binding, we can at the time of binding by giving the Withpropertyvalue method to specify the value of the Discountsize property, as shown in the following code:


public static void Main() {

    IKernel ninjectKernel = new StandardKernel();

 

    ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();

    ninjectKernel.Bind<IDiscountHelper>()

        .To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 5M);

 

    IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();

    ShoppingCart cart = new ShoppingCart(calcImpl);

    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

    Console.ReadKey();

}

Just after the bind and to method add a Withpropertyvalue method, the other code does not change, and once again witnessed the efficient use of ninject coding.

The Withpropertyvalue method receives two parameters, one is the property name ("Discountsize" in the example), and one is the property value (5 in the example). The results of the operation are as follows:






If you want to assign values to multiple properties, you can add multiple Withpropertyvalue (< property name >,< property value >) methods after bind and to mode.

We can also pass arguments to the class's constructors when the class is instantiated. To illustrate, let's Change the Defaultdiscounthelper class:


public class DefaultDiscountHelper : IDiscountHelper { 

    private decimal discountRate; 

  

    public DefaultDiscountHelper(decimal discountParam) { 

        discountRate = discountParam; 

    } 

  

    public decimal ApplyDiscount(decimal totalParam) { 

        return (totalParam - (discountRate/ 10m * totalParam)); 

    } 

}


Obviously, the Defaultdiscounthelper class must pass a parameter to the constructor when instantiating it, or the program will make an error. Similar to assigning a value to a property, the only way to do this is withconstructorargument (< parameter name >,< argument value >), as shown in the following code:

...

ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); 

ninjectKernel.Bind<IDiscountHelper>() 

    .To< DefaultDiscountHelper>().WithConstructorArgument("discountParam", 5M);

...


Again, you just need to change one line of code, how the other code was written or how it was written. If the constructor has more than one parameter, you need to add multiple withconstructorargument to the bind and to methods.

3. Self-binding

One of the most useful features of niject is self-binding. After binding interfaces and classes through the bind and to methods, you can obtain an instance of a class directly from the ninjectkernel.get< class name > ().

In the previous few examples, we created the ShoppingCart class instance as follows:


... Ivaluecalculator Calcimpl = ninjectkernel.get<ivaluecalculator> (); ShoppingCart cart = new ShoppingCart (CALCIMPL);


In fact, there is a simpler method of determining the following:


... ShoppingCart cart = ninjectkernel.get<shoppingcart> (); ...


This kind of writing does not need to care about which interface the ShoppingCart class relies on, nor does it need to manually get the implementation of the interface (CALCIMPL). When you request an instance of the ShoppingCart class through this code, Ninject automatically determines the dependencies and creates the implementation for us for the desired interface. This kind of way looks a bit strange, in fact, the wording is:


...ninjectkernel.bind<shoppingcart> (). Toself (); ShoppingCart cart = ninjectkernel.get<shoppingcart> ();..


There is a toself method for self-binding here, which can be omitted in this example. But the benefit of self-binding with the Toself method is that you can specify the values of the constructor parameters, attributes, and so on later in the Withxxx method.

4. Derived class bindings

through a generic binding, when requesting an implementation of an interface, Ninject helps us automatically create an instance of the class that implements the interface. We say that such a class to implement such an interface, can also say that such a class inherits a certain interface. If we treat the interface as a parent class, can we also bind the parent class to a subclass that inherits from that parent class? Let's experiment with one. First, modify the ShoppingCart class, and change its Calculatestockvalue method to virtual method:


Public class ShoppingCart {

     Protected IValueCalculator calculator;

     Protected Product[] products;

 

     / / Constructor, the parameter is an instance of the class that implements the IEmailSender interface

     Public ShoppingCart(IValueCalculator calcParam) {

         Calculator = calcParam;

         Products = new[]{

             New Product {Name = "Watermelon", Category = "Fruit", Price = 2.3M},

             New Product {Name = "Apple", Category = "Fruit", Price = 4.9M},

             New Product {Name = "Water Spinach", Category = "Vegetable", Price = 2.2M},

             New Product {Name = "Sweet", Category = "Vegetable", Price = 1.9M}

         };

     }

 

     / / Calculate the total price of goods in the shopping cart

     Public virtual decimal CalculateStockValue() {

         / / Calculate the total price of the goods

         Decimal totalValue = calculator.ValueProducts(products);

         Return totalValue;

     }

}


Then add a subclass of the ShoppingCart class:


Public class LimitShoppingCart : ShoppingCart {

     Public LimitShoppingCart(IValueCalculator calcParam)

         : base(calcParam) {

     }

 

     Public override decimal CalculateStockValue() {

         //Filter products whose price exceeds the upper limit

         Var filteredProducts = products.Where(e => e.Price < ItemLimit);

 

         Return calculator.ValueProducts(filteredProducts.ToArray());

     }

 

     Public decimal ItemLimit { get; set; }

}


Then bind the parent class ShoppingCart to the subclass Limitshoppingcart:


Public static void Main() {

     IKernel ninjectKernel = new StandardKernel();

 

     ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();

     ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()

         .WithPropertyValue("DiscountSize", 5M);

     / / Derived class binding

     ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()

         .WithPropertyValue("ItemLimit", 3M);

 

     ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

     Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

     Console.ReadKey();

}


Operation Result:






As can be seen from the running result, the Cart object calls the Calculatestockvalue method of the subclass, proving that the parent class can be bound to a subclass that inherits from the parent class. By deriving class bindings, when we request the parent class, Ninject automatically creates an instance of the corresponding subclass and returns it. Because abstract classes cannot be instantiated, derived class bindings are useful when using abstract classes.

5. Conditional binding

When an interface has multiple implementations or a class has multiple subclasses, we can specify which implementation or subclass to use through conditional binding. To demonstrate, we add another implementation to the Ivaluecalculator interface, as follows:


public class IterativeValueCalculator : IValueCalculator { 

  

    public decimal ValueProducts(params Product[] products) { 

        decimal totalValue = 0; 

        foreach (Product p in products) { 

            totalValue += p.Price; 

        } 

        return totalValue; 

    } 

}


The Ivaluecalculator interface now has two implementations: Iterativevaluecalculator and Linqvaluecalculator. We can specify that if the implementation of the interface is injected into the Limitshoppingcart class, then the Iterativevaluecalculator is used, and linqvaluecalculator is used in all other cases. As shown below:


Public static void Main() {

     IKernel ninjectKernel = new StandardKernel();

 

     ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();

     ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()

         .WithPropertyValue("DiscountSize", 5M);

     / / Derived class binding

     ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()

         .WithPropertyValue("ItemLimit", 3M);

     //condition binding

     ninjectKernel.Bind<IValueCalculator>()

         .To<IterativeValueCalculator>().WhenInjectedInto<LimitShoppingCart>();

 

     ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();

     Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());

     Console.ReadKey();

}


Operation Result:






Using Ninject in ASP. NET MVC



This article demonstrates the use of Ninject with a console application, but it is still a bit complicated to integrate ninject into ASP. The first thing to do is to create a class that inherits System.Web.Mvc.DefaultControllerFactory, and MVC uses this class by default to create an instance of the Controller class (which is specifically the case for subsequent posts). The code is as follows:


Ninjectcontrollerfactory

Now for the moment do not explain this code, everyone can read and read, do not understand, as long as you know in ASP. NET MVC to do such a thing ninject.

After adding this class, one more thing to do is to register the class in the MVC framework. In general, we register in the Application_Start method in the Global.asax file as follows:

protected void Application_Start() {

    AreaRegistration.RegisterAllAreas();

 

    WebApiConfig.Register(GlobalConfiguration.Configuration);

    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

    RouteConfig.RegisterRoutes(RouteTable.Routes);

 

    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

}



Once registered, the MVC framework uses the Ninjectcontrollerfactory class to get an instance of the Cotroller class. In a follow-up blog post that shows you how to use Ninject in ASP. NET MVC, this is not a concrete demonstration, and you know you need to do two things.

Although we have taken a lot of effort to learn ninject in order to use such a ninjectcontrollerfactory class in MVC, it is necessary to understand how ninject works. Understanding a DI container makes it easier and more efficient to develop and test.



The above is [ASP.]04-Dependency Injection (DI) and ninject content, for more information, please follow topic.alibabacloud.com (www.php.cn)!


  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.