# 第3章：依赖注入

#### 任务16：介绍 <a href="#ren-wu-16-jie-shao" id="ren-wu-16-jie-shao"></a>

1、依赖注入概念详解

* 从UML和软件建模来理解
* 从单元测试来理解

2、ASP.NET Core 源码解析

#### 任务17：从UML角度来理解依赖 <a href="#ren-wu-17-cong-uml-jiao-du-lai-li-jie-yi-lai" id="ren-wu-17-cong-uml-jiao-du-lai-li-jie-yi-lai"></a>

**1、什么是依赖**

当一个类A完成某个任务需要另一个类B来帮助时，A就对B产生了依赖

例如CustomerController需要对customer进行新增或查找时用到EF,则对EF的Context产生了依赖

```
var context = new CustomerContext(new DbContextOptions<CustomerContext>{});
```

**2、显示依赖与隐式依赖**

显示依赖：把一个类用到的所有外部组件放到一个类最上面，在构造函数里面初始化

```
private CustomerContext _context;

public CustomerController()
{
    _context = new CustomerContext(new DbContextOptions<CustomerContext>{});
}
```

隐式依赖：需要用到的地方再初始化，不推荐

```
var context = new CustomerContext(new DbContextOptions<CustomerContext>{});
```

**3、依赖倒置原则**

依赖高层业务，不依赖低层业务的具体实现，而依赖于具体的抽象

CustomerController是高层业务的一个组件，依赖于CustomerContext是一个低层数据库的实现，如果现在需要把EF换成一个内存的实现或者mysql，需要修改CustomerController类，风险很大，所以应该依赖于低层业务的抽象

把低层业务方法抽象，比如查找，新增，抽象出一个接口，当不需要使用EF的时候，使用内存的实现替换

```
private ICustomerRepository _customerRepository;

public CustomerController()
{
    _customerRepository = new EfCustomerRepository(
        new CustomerContext(new DbContextOptions<CustomerContext>{}));
}
```

#### 任务18：控制反转 <a href="#ren-wu-18-kong-zhi-fan-zhuan" id="ren-wu-18-kong-zhi-fan-zhuan"></a>

实现依赖注入的方式不由自己决定，而是交给一个IOC容器，需要什么由容器传入，比如生产环境需要使用EF，则由容器传入一个EfCustomerRepository，而测试环境需要使用内存级别的，则传入一个MemoryCustomerRepository

```
private ICustomerRepository _customerRepository;

public CustomerController(ICustomerRepository customerRepository)
{
    _customerRepository = customerRepository;
}
```

#### 任务19：单元测试 <a href="#ren-wu-19-dan-yuan-ce-shi" id="ren-wu-19-dan-yuan-ce-shi"></a>

```
var repository = new Data.MemoryCustomerRepository();
var controller = new CustomerController(repository);// 通过外部控制Controller里面的依赖

var customer = new Model.Customer()
{
    FirstName = "Mingson",
    LastName = "Zheng",
    Phone = "123456789",
};

var result = controller.Add(customer);
Assert.IsType<OkResult>(result);// 正确结果

var resultBad = controller.Add(customer);
Assert.IsType<BadRequestObjectResult>(resultBad);// 错误结果
```

通过单元测试可以得知修改Bug过程中是否误删代码，导致原来通过的测试现在无法通过。

#### 任务20：DI初始化的源码解读 <a href="#ren-wu-20di-chu-shi-hua-de-yuan-ma-jie-du" id="ren-wu-20di-chu-shi-hua-de-yuan-ma-jie-du"></a>

Microsoft.AspNetCore.Hosting.WebHostBuilder

```
    /// <summary>
    /// Builds the required services and an <see cref="T:Microsoft.AspNetCore.Hosting.IWebHost" /> which hosts a web application.
    /// </summary>
    public IWebHost Build()
    {

      ......
      
      // 第一步，build
      
      IServiceCollection serviceCollection1 = this.BuildCommonServices(out hostingStartupErrors);
      
      // 第二步，获取ServiceCollection,ServiceProvider
      
      IServiceCollection serviceCollection2 = serviceCollection1.Clone();
      IServiceProvider providerFromFactory = GetProviderFromFactory(serviceCollection1);
      
      ......
      
      // 第三步，new一个WebHost，传入ServiceCollection，ServiceProvider
      
      WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors);
      
      ......
      
      // 第四步，webHost初始化方法Initialize
      
        webHost.Initialize();
        
        ......

    }
```

第一步BuildCommonServices中new一个ServiceCollection就是在startup接口中使用

```
    private IServiceCollection BuildCommonServices(
      out AggregateException hostingStartupErrors)
    {

      ......
      
      ServiceCollection services = new ServiceCollection();
      
      // new完之后添加一些初始化操作
      
      ......

      return (IServiceCollection) services;
    }
```

IStartup接口

```
namespace Microsoft.AspNetCore.Hosting
{
  public interface IStartup
  {
    IServiceProvider ConfigureServices(IServiceCollection services);

    void Configure(IApplicationBuilder app);// 配置管道
  }
}
```

第四步，webHost初始化方法Initialize

```
    public void Initialize()
    {
      
        ......
      
        this.EnsureApplicationServices();

        ......

    }

    private void EnsureApplicationServices()
    {
      
      ......
      
      this.EnsureStartup();
      this._applicationServices = this._startup.ConfigureServices(this._applicationServiceCollection);
    }
    
    private void EnsureStartup()
    {
      if (this._startup != null)
        return;
      this._startup = this._hostingServiceProvider.GetService<IStartup>();
      if (this._startup == null)
        throw new InvalidOperationException(string.Format("No startup configured. Please specify startup via WebHostBuilder.UseStartup, WebHostBuilder.Configure, injecting {0} or specifying the startup assembly via {1} in the web host configuration.", (object) "IStartup", (object) "StartupAssemblyKey"));
    }
```

#### 任务21：依赖注入的使用 <a href="#ren-wu-21-yi-lai-zhu-ru-de-shi-yong" id="ren-wu-21-yi-lai-zhu-ru-de-shi-yong"></a>

了解ASP.NET Core 依赖注入，看这篇就够了：

<http://www.jessetalk.cn/2017/11/06/di-in-aspnetcore/>
