DI container Ninject instances for managing interfaces and implementations, base classes and derived classes, and implementing dependency Injection

Source: Internet
Author: User

When a class depends on another specific class, it is easy to form a "strong coupling" relationship between the two. We usually abstract an interface based on a specific class and then let the class depend on this interface. This forms a "loose coupling" relationship, which is conducive to application expansion. We can use DI container and Dependency Injection container, that is, Dependency Injection container to manage interfaces and implementation classes. The so-called "dependency injection" means that when a class needs to be used or depends on the Implementation class of an interface class, the interface is injected to the constructor or attribute of the class through the DI container API, and then the injection interface method is called. The DI container depends on the registered dependency chain, you can either automatically execute an interface implementation class method or use its API to selectively execute an interface implementation class method. This article describes how to use the DI container Ninject.

 

This article includes:

  • Ninject manages simple interfaces and implementation classes, and automatically executes interface implementation class methods.
  • Ninject manages nested interfaces and implementation classes, and automatically executes interface implementation class methods.
  • Ninject setting interface implementation class property value
  • Ninject sets the construction parameter value of the interface implementation class
  • Bind Ninject classes
  • Ninject binds the base class and derived class, and sets the attribute value of the derived class.
  • Ninject condition binding

 

Ninject manages simple interfaces and implementation classes, and automatically executes interface implementation class methods.

Demand: one shopping cart class calculates the total price of all products

Related Products:

public class Product    {        public int Id { get; set; }        public string Name { get; set; }        public decimal Price { get; set; }    }

You need a help class for calculating the total product price. However, considering the scalability, you must first write an interface for calculating the total product price:

public interface IValueCalculator    {        decimal ValueProducts(params Product[] products);    }

Then write the implementation class of this interface for calculating the total price, and implement it using the Linq method:

public class LinqValueCalculator : IValueCalculator    {        public decimal ValueProducts(params Product[] products)        {            return products.Sum(p => p.Price);        }    }

Finally, the IValueCalculator is injected into the shopping cart class through its constructor:

public class ShoppingCart    {        private IValueCalculator calculator;        public ShoppingCart(IValueCalculator calc)        {            calculator = calc;        }        public decimal TotalValue()        {            Product[] products =            {                new Product(){Id = 1, Name = "Product1", Price = 85M},                 new Product(){Id = 2, Name = "Product2", Price = 90M}            };            return calculator.ValueProducts(products);        }    }

When using NuGet to install Ninject, when calling the client, you must first register the interface and implement the class dependency chain. When you need the interface, you can obtain this interface from the DI container.

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); IValueCalculator calculator = ninjectKernel. get <IValueCalculator> (); ShoppingCart cart = new ShoppingCart (calculator); Console. writeLine ("total price: {0}", cart. totalValue (); Console. readKey ();}}

Result: 175
We can see that we only inject the interface into the ShoppingCart class. when calling the interface method, the DI container will automatically help us find the implementation class of this interface and call the method.

 

Ninject manages nested interfaces and implementation classes, and automatically executes interface implementation class methods.

Demand: one shopping cart class calculates the total price of all products and discounts

Analysis: when calculating the total price of all products, the ShoppingCart class depends on the total price calculation class, but when discounts are required, the implementation class of the total price calculation also depends on a discount interface. No matter how the dependency is nested, we only need to hand over the interface and implementation class to Ninject, And the other things are easily done by Ninject.

 

There may be many discount methods. Here is also an extension point, so the first discount interface:

public interface IDiscountHelper    {        decimal ApplyDiscount(decimal total);    }

Off by default:

public class DefaultDiscountHelper : IDiscountHelper    {        public decimal ApplyDiscount(decimal total)        {            return (total - 10m/100m*total);        }    }

The implementation class for calculating the total price depends on this discount interface:

public class LinqValueCalculator : IValueCalculator    {        private IDiscountHelper discounter;        public LinqValueCalculator(IDiscountHelper discountParam)        {            this.discounter = discountParam;        }        public decimal ValueProducts(params Product[] products)        {            return discounter.ApplyDiscount(products.Sum(p => p.Price));        }    }

The client now needs to register the relationship between the discount interface and the implementation class:

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (); IValueCalculator calculator = ninjectKernel. get <IValueCalculator> (); ShoppingCart cart = new ShoppingCart (calculator); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 157.5
It can be seen that once all the interfaces and implementation class relationships are registered in Ninject, Ninject will automatically find the implementation class for calculating the total price when calling the calculation total price interface method, then, the discount implementation class is automatically found.

 

Ninject setting interface implementation class property value

Demand: one shopping cart class calculates the total price of all products and discounts. the discount amount can be dynamically set.

Analysis: adds an attribute to the discount implementation class. When Ninject registers the interface and the implementation class, it assigns the initial value to the attribute.

 

Add an attribute to the discount interface implementation class:

public class DefaultDiscountHelper : IDiscountHelper    {        public decimal DiscountSize { get; set; }        public decimal ApplyDiscount(decimal total)        {            return (total - DiscountSize/100m*total);        }    }

In client applications, assign an initial value to DiscountSize when registering interfaces and implementation classes.

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (). withPropertyValue ("DiscountSize", 50 M); IValueCalculator calculator = ninjectKernel. get <IValueCalculator> (); ShoppingCart cart = new ShoppingCart (calculator); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 87.5

 

Ninject sets the construction parameter value of the interface implementation class

This is still the demand: a shopping cart class calculates the total price of all products and discounts, the discount amount can be dynamically set

Add constructors and private variables to the discount interface implementation class:

public class DefaultDiscountHelper : IDiscountHelper    {        private decimal discountRate;        public DefaultDiscountHelper(decimal discountParam)        {            discountRate = discountParam;        }        public decimal ApplyDiscount(decimal total)        {            return (total - discountRate/100m*total);        }    }

In client applications, when registering interfaces and implementation classes, assign initial values to the constructor parameters:

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (). withConstructorArgument ("discountParam", 50 M); IValueCalculator calculator = ninjectKernel. get <IValueCalculator> (); ShoppingCart cart = new ShoppingCart (calculator); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 87.5

 

Bind Ninject classes

Ninject classes are bound by themselves. In terms of text, it seems a bit confusing. We can understand this as follows:

 

When Ninject registers interfaces and implementation classes, we can Get the interfaces through IValueCalculator calculator = ninjectKernel. Get <IValueCalculator>. ShoppingCart depends on IValueCalculator. Although ShoppingCart is not an interface type, whether or not ShoppingCart can be set by using ShoppingCart cart = ninjectKernel. get <ShoppingCart> () to Get the instance of ShoppingCart?

 

The answer is: yes. We need to bind ShoppingCart to Ninject:

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (). withConstructorArgument ("discountParam", 50 M); ninjectKernel. bind <ShoppingCart> (). toSelf (); ShoppingCart cart = ninjectKernel. get <ShoppingCart> (); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 87.5

 

Ninject binds the base class and derived class, and sets the attribute value of the derived class.

The new requirement is as follows: a shopping cart class calculates the total price of all products and discounts. the discount amount can be dynamically set, and an upper limit is set for the calculated product price.

 

Analysis: ShoppingCar can be designed as the base class, and the total price calculation method can be designed as virtual. In the ShoppingCart derived class, add an attribute about the maximum price, rewrite the total price calculation method in the base class ShoppingCart to take the product price ceiling into account.

 

ShoppingCart is used as the base class, and the total price calculation method is set to virtual:

public class ShoppingCart    {        protected IValueCalculator calculator;        protected Product[] products;        public ShoppingCart(IValueCalculator calc)        {            calculator = calc;            products = new[]            {                new Product(){Id = 1, Name = "Product1", Price = 85M},                 new Product(){Id = 2, Name = "Product2", Price = 90M}            };        }        public virtual decimal TotalValue()        {                      return calculator.ValueProducts(products);        }    }


ShoppingCart's derived class overwrites the total price calculation method and considers the price ceiling:

public class LimitPriceShoppingCart : ShoppingCart    {        public LimitPriceShoppingCart(IValueCalculator calc) : base(calc){}        public override decimal TotalValue()        {            var filteredProducts = products.Where(e => e.Price < PriceLimit);            return calculator.ValueProducts(filteredProducts.ToArray());        }        public decimal PriceLimit { get; set; }    }

The base class and derived class are registered in Ninject and assigned an initial value to the properties of the derived class:

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (). withConstructorArgument ("discountParam", 50 M); ninjectKernel. bind <ShoppingCart> (). to <LimitPriceShoppingCart> (). withPropertyValue ("PriceLimit", 88 M); ShoppingCart cart = ninjectKernel. get <ShoppingCart> (); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 42.5

 

Ninject condition binding

That is, an interface can have multiple implementations, And the Ninject API specifies that some interfaces are used as implementation classes under certain conditions.

 

For example, add another class to the price calculation interface to calculate the price by traversing the combination:

public class IterativeValueCalculator : IValueCalculator    {        public decimal ValueProducts(params Product[] products)        {            decimal result = 0;            foreach (var item in products)            {                result += item.Price;            }            return result;        }    }

The following code specifies the total price to be calculated when LimitPriceShoppingCart is injected:

Class Program {static void Main (string [] args) {IKernel ninjectKernel = new StandardKernel (); ninjectKernel. bind <IValueCalculator> (). to <LinqValueCalculator> (); ninjectKernel. bind <IValueCalculator> (). to <IterativeValueCalculator> (). whenInjectedInto <LimitPriceShoppingCart> (); ninjectKernel. bind <IDiscountHelper> (). to <DefaultDiscountHelper> (). withConstructorArgument ("discountParam", 50 M); ninjectKernel. bind <ShoppingCart> (). to <LimitPriceShoppingCart> (). withPropertyValue ("PriceLimit", 88 M); ShoppingCart cart = ninjectKernel. get <ShoppingCart> (); Console. writeLine ("total price: {0}", cart. totalValue (); // The 175 Console. readKey ();}}

Result: 85

 

Summary

The Ninject DI container can help us manage interfaces and implementations, base classes and derived classes, and provide some APIs, this allows us to set the initial value of the constructor parameter for the implementation class of the interface or the derived class of the base class, set the initial value of the attribute, and even set the condition to use a specific implementation, that is, condition binding.

 

References:
Proficient in ASP. NET MVC3 framework (Third edition)

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.