MVC 5 + EF6 Full Tutorial 15-decoupling with Di

Source: Internet
Author: User
Tags webhost

Original: MVC 5 + EF6 Full Tutorial 15-decoupling with Di

If you look at some open source projects, you'll find ubiquitous Di (Dependency injection Dependency injection).
This article will detail how to use Ninject in MVC to implement DI

Article outline
    • Scenario Description & Problem Extraction
    • First-round refactoring
    • Introduction of Ninject
    • Second round reconstruction
    • Summarize
Scenario Description & Problem Extraction

DI is a design pattern that implements decoupling of components.
First simulate a scene to elicit the problem, we directly use the Ninject official website example: A group of warriors fight for glory.
First, we need a suitable weapon to equip these warriors.

class Sword {    public void Hit(string target)    {        Console.WriteLine("Chopped {0} clean in half", target);    }}

Second, we define the warrior class.
Warriors have a attack () method that is used to attack the enemy.

class Samurai{    readonly Sword sword;    public Samurai()    {        this.sword = new Sword();    }        public void Attack(string target)    {        this.sword.Hit(target);    }}

Now we can create a warrior to fight.

class Program{    public static void Main()    {        var warrior = new Samurai();        warrior.Attack("the evildoers");    }}

We run this program and will print out chopped the evildoers clean in half
Now begs the question: what if we want to equip samurai with different weapons?
Because Sword is created in the constructor of the Samurai class, you must change Samurai.
Obviously the coupling between Samurai and Sword is too high, we first define an interface to decouple it.

First-round refactoring

The first step is to establish a loosely coupled component: By introducing Iweapon, there is no direct dependency between program and sword.

interface IWeapon{    void Hit(string target);}

Modifying the Sword class

class Sword : IWeapon{    public void Hit(string target)    {        Console.WriteLine("Chopped {0} clean in half", target);    }}

Modify the Samurai class, move the Sword in the original constructor to the constructor's arguments, replace it with an interface, and then we can inject Sword through the Samurai constructor, which is an example of a Di (via constructor injection).

class Samurai{    readonly IWeapon weapon;    public Samurai(IWeapon weapon)    {        this.weapon = weapon;    }        public void Attack(string target)    {        this.weapon.Hit(target);    }}

If we need to use other weapons, we don't need to modify the samurai. We'll create another weapon.

class Shuriken : IWeapon{    public void Hit(string target)    {        Console.WriteLine("Pierced {0}'s armor", target);    }}

Now we can create warriors armed with different weapons.

class Program{    public static void Main()    {        var warrior1 = new Samurai(new Shuriken());        var warrior2 = new Samurai(new Sword());        warrior1.Attack("the evildoers");        warrior2.Attack("the evildoers");    }}

Print out the following results:

Pierced the evildoers armor.
Chopped the evildoers clean in half.
Now that the dependency problem has been resolved, the above approach is called manual dependency injection.
Each time you need to create a samurai, you must first create an implementation of the Iweapon interface and then pass it to the Samurai constructor.
But how do you instantiate a specific implementation of an interface without having to create a dependency somewhere in the application? As it is now, the following statements are still needed somewhere in the application.

IWeapon weapon = new Sword();var warrior = new Samurai(weapon);

This actually moves the dependency back, and when instantiated, it needs to be modified in the program, which destroys the goal of replacing a weapon without having to modify program.
The effect we need to achieve is to get an object that implements an interface without having to create the object directly, that is, automatic dependency injection.
The solution is to use the dependency injection Container, Di container.
In the example above, it acts as a middleware between the dependencies declared by the class (program) and the classes (Sword) that are used to resolve those dependencies.
You can use a DI container to register an interface or an abstract type to be used by a group of applications, and to indicate which implementation class is required to instantiate the dependency. Therefore, in the example above, the Iweapon interface is registered with the DI container and indicates that an instance of sword should be created when the iweapon needs to be implemented. The DI container combines the two information to create a sword object, which is then used as a parameter to create the program, so that the sword can be used in the application.
Next, we demonstrate how to use the Di container of Ninject.

Introduction of Ninject

To facilitate testing in MVC, we made a slight adjustment to the previous class.
The following files are created in the Models folder:

namespace XEngine.Web.Models{    public interface IWeapon    {        string Hit(string target);    }}namespace XEngine.Web.Models{    public class Sword:IWeapon    {        public string Hit(string target)        {            return string.Format("Chopped {0} clean in half", target);        }    }}namespace XEngine.Web.Models{    public class Shuriken:IWeapon    {        public string Hit(string target)        {            return string.Format("Pierced {0}'s armor", target);        }    }}namespace XEngine.Web.Models{    public class Samurai    {        readonly IWeapon weapon;        public Samurai(IWeapon weapon)        {            this.weapon = weapon;        }        public string Attack(string target)        {            return this.weapon.Hit(target);        }    }}

Add an action to the test HomeController.cs file

public ActionResult Battle(){    var warrior1 = new Samurai(new Sword());    ViewBag.Res = warrior1.Attack("the evildoers");    return View();}

Finally, the action corresponds to the view

@{    Layout = null;}<!DOCTYPE html>

Run will see the string: Chopped the evildoers clean in half
OK, the preparation is OK, we will introduce Ninject

First, add Ninject to the project

Select Tools, Library Package Manager, Package Manager Console in VS
Enter the following command:

install-package ninjectinstall-package Ninject.Web.Common

The results of the operation are as follows:

PM> install-package ninject正在安装“Ninject 3.2.2.0”。您正在从 Ninject Project Contributors 下载 Ninject,有关此程序包的许可协议在 https://github.com/ninject/ninject/raw/master/LICENSE.txt 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。已成功安装“Ninject 3.2.2.0”。正在将“Ninject 3.2.2.0”添加到 XEngine.Web。已成功将“Ninject 3.2.2.0”添加到 XEngine.Web。PM> install-package Ninject.Web.Common正在尝试解析依赖项“Ninject (≥ 3.2.0.0 && < 3.3.0.0)”。正在安装“Ninject.Web.Common 3.2.3.0”。您正在从 Ninject Project Contributors 下载 Ninject.Web.Common,有关此程序包的许可协议在 https://github.com/ninject/ninject.extensions.wcf/raw/master/LICENSE.txt 上提供。请检查此程序包是否有其他依赖项,这些依赖项可能带有各自的许可协议。您若使用程序包及依赖项,即构成您接受其许可协议。如果您不接受这些许可协议,请从您的设备中删除相关组件。已成功安装“Ninject.Web.Common 3.2.3.0”。正在将“Ninject.Web.Common 3.2.3.0”添加到 XEngine.Web。已成功将“Ninject.Web.Common 3.2.3.0”添加到 XEngine.Web。

After the installation is complete, we can modify the action method in the HomeController

Second, the use of Ninject to complete the binding function

The basic functions are divided into three steps:
Create the kernel, configure the kernel (Specify interfaces and require binding classes), create concrete objects
Specific as follows:

public ActionResult Battle(){    //var warrior1 = new Samurai(new Sword());    //1. 创建一个Ninject的内核实例    IKernel ninjectKernel = new StandardKernel();    //2. 配置Ninject内核,指明接口需绑定的类    ninjectKernel.Bind<IWeapon>().To<Sword>();    //3. 根据上一步的配置创建一个对象    var weapon=ninjectKernel.Get<IWeapon>();    var warrior1 = new Samurai(weapon);    ViewBag.Res = warrior1.Attack("the evildoers");    return View();}

Look at the results in the view below, exactly the same as in the beginning

The classes that the interface specifically needs to instantiate are obtained through get, which, by literal means, should be easy to understand, and I don't have much to explain.
We completed the first step in using the Ninject transformation, but the interface and implementation class bindings are still defined in the HomeController, and we'll do a round of refactoring to get rid of these configurations in HomeController.

Second round reconstruction

Automatic dependency injection is achieved by creating and registering the dependency resolver.

First, create a dependency resolver

The job of the dependency resolver here is to create a specific object by creating the kernel, configuring the kernel (specifying interfaces and binding classes), and ninject the basic functionality of the previous three steps. We implement the dependency resolver by implementing the Idependencyresolver interface under the System.mvc namespace.
Interfaces to be implemented:

namespace System.Web.Mvc{    // 摘要:     //     定义可简化服务位置和依赖关系解析的方法。    public interface IDependencyResolver    {        // 摘要:         //     解析支持任意对象创建的一次注册的服务。        //        // 参数:         //   serviceType:        //     所请求的服务或对象的类型。        //        // 返回结果:         //     请求的服务或对象。        object GetService(Type serviceType);        //        // 摘要:         //     解析多次注册的服务。        //        // 参数:         //   serviceType:        //     所请求的服务的类型。        //        // 返回结果:         //     请求的服务。        IEnumerable<object> GetServices(Type serviceType);    }}

Specific implementation:

namespace XEngine.Web.Infrastructure{    public class NinjectDependencyResolver:IDependencyResolver    {        private IKernel kernel;        public NinjectDependencyResolver(IKernel kernelParam)        {            kernel = kernelParam;            AddBindings();        }        public object GetService(Type serviceType)        {            return kernel.TryGet(serviceType);        }        public IEnumerable<object> GetServices(Type serviceType)        {            return kernel.GetAll(serviceType);        }        private void AddBindings()        {            kernel.Bind<IWeapon>().To<Sword>();        }    }}

The MVC framework calls the GetService or GetServices method when a class instance is required to service an incoming request. The job of the dependency resolver is to create this instance.

Second, register the dependency resolver

The last step remains, register the dependency resolver.
Open the package Manager Console again
Enter the following command:

install-package Ninject.MVC5

Run results

Pm> Install-package Ninject.mvc5 is trying to parse the dependency "Ninject (≥3.2.0.0 && < 3.3.0.0)". Attempting to resolve dependency "Ninject.Web.Common.WebHost (≥3.0.0.0)". Attempting to resolve dependency "Ninject.Web.Common (≥3.2.0.0 && < 3.3.0.0)". Attempting to resolve dependency "Webactivatorex (≥2.0 && < 3.0)". Attempting to resolve dependency "Microsoft.Web.Infrastructure (≥1.0.0.0)". Installing "Webactivatorex 2.0". "Webactivatorex 2.0" was successfully installed. Installing "Ninject.Web.Common.WebHost 3.2.0.0". You are downloading Ninject.Web.Common.WebHost from Ninject Project Contributors, the license agreement for this package is in https://github.com/ninject/ Available on Ninject.web.common/raw/master/license.txt. Check to see if there are other dependencies on this package that may have their own license agreement. Your use of the package and dependencies constitutes your acceptance of the license agreement. If you do not accept these license agreements, please remove the relevant components from your device. "Ninject.Web.Common.WebHost 3.2.0.0" was successfully installed. Installing "Ninject.mvc5 3.2.1.0". You are downloading ninject.mvc5 from Remo Gloor, Ian Davis, and the license agreement for this package is in https://github.com/ninject/ninject.web.mvc/raw/master/mvc3/ Available on LICENSE.txt. Check to see if there are other dependencies on this package that may have their own license agreement. Your use of the package and dependencies constitutes your acceptance of the license agreement. If you do not accept these license agreements, please remove the relevant components from your device. "Ninject.mvc5 3.2.1.0" was successfully installed. Adding "Webactivatorex 2.0" to Xengine.web. The WebA has been successfullyCtivatorex 2.0 "added to Xengine.web. Adding "Ninject.Web.Common.WebHost 3.2.0.0" to Xengine.web. "Ninject.Web.Common.WebHost 3.2.0.0" was successfully added to Xengine.web. Adding "Ninject.mvc5 3.2.1.0" to Xengine.web. "Ninject.mvc5 3.2.1.0" was successfully added to Xengine.web.

You can see that the App_start folder has more than one NinjectWebCommon.cs file, which defines some of the methods that are automatically called when the application starts, integrating them into the request life cycle of ASP.

Find the last method registerservices, only need to add a sentence.

public static class NinjectWebCommon {    /// <summary>    /// Load your modules or register your services here!    /// </summary>    /// <param name="kernel">The kernel.</param>    private static void RegisterServices(IKernel kernel)    {        System.Web.Mvc.DependencyResolver.SetResolver(new XEngine.Web.Infrastructure.NinjectDependencyResolver(kernel));    }        }
Iii. reconstruction of HomeController

Basically add a constructor to receive the implementation of the interface, as follows

private IWeapon weapon;public HomeController(IWeapon weaponParam){    weapon = weaponParam;}public ActionResult Battle(){    //var warrior1 = new Samurai(new Sword());    ////1. 创建一个Ninject的内核实例    //IKernel ninjectKernel = new StandardKernel();    ////2. 配置Ninject内核,指明接口需绑定的类    //ninjectKernel.Bind<IWeapon>().To<Sword>();    ////3. 根据上一步的配置创建一个对象    //var weapon=ninjectKernel.Get<IWeapon>();    var warrior1 = new Samurai(weapon);    ViewBag.Res = warrior1.Attack("the evildoers");    return View();}

Running can see the same effect as before.
This dependency is injected into homecontroller in the run, which means that an instance of the implementation class of the Iweapon interface is created during the instantiation of the class and passed to the HomeController constructor. HomeController the implementation class of the dependency interface does not have a compile-time dependency directly.
We can use another weapon without any modification to the HomeController.

Summarize

DI is a design pattern that implements decoupling of components. Divided into two steps:

    1. Interrupting and declaring dependencies
      Create a class constructor that takes the implementation of the desired interface as its parameter and removes the dependency on the specific class.
    2. Injection dependency
      Automatic dependency injection is achieved by creating and registering the dependency resolver.

Dependency Injection In addition to the way through the constructor can also through the property injection and method injection, expand talk there are many things, we still follow the usual style, enough on the good, first take everyone clear the obstacles, we first directly imitate the realization of the good.
Further study can refer to the official website study Tutorial: Https://github.com/ninject/Ninject/wiki
Follow-up article Project REAL-time parts, according to the actual needs of the project, when used to expand the talk.

Wish Learning Progress:)

MVC 5 + EF6 Full Tutorial 15-decoupling with Di

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.