# 第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/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mingsonzheng.gitbook.io/aspnetcore-quick-start/di-3-zhang-yi-lai-zhu-ru.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
