DI的一些事

传送门马丁大叔的文章

什么是依赖注入(DI: Dependency Injection)?

依赖注入(DI)是一种面向对象的软件设计模式,主要是帮助开发人员开发出松耦合的应用程序。同时呢,让应用更容易进行单元测试和维护。

DI其实就是用一个注入器类为一个对象提供其依赖的一个过程!如何更好的理解呢?下面就举个列子解释下!

比如 class Client,它要使用服务class Service的提供的功能,这个时候就说Service是Client的依赖,程序实现如下:

 var s = new Service();
var c = new Client(s);

很明显我们还要承担创建Service的对象的职责,程序出现了强耦合问题,后面如果需求变化,我们要替换掉Service,那我们就要修改这边的代码,这样的程序很面明,扩展性,灵活性比较差了!

引入DI之后呢,我们应该还有一个注入器类,假设是 class Injector 。为了更好的解释DI的好处,上面的代码我们重新设定为 class Client 依赖 接口IService , class Service 实现了IService ,这个时候我们的程序主流程不需要关注如何创建的Service,可以把这部分的职责委托给Injector,我们只要告诉Injector,我需要IService,请提供给我,程序实现如下:

var s = Injector.Get(typeof(IService));
var c = new Client(s);

这样的好处就很明显了,我们只关注自己的核心业务职责,对应依赖如何创建的,具体是什么类实现的,都不用自己管了,权力交给注入器就可以了!

 划重点:其实上面这个过程大家应该发现了我们把本来自己的一部分控制权,转交给了注入器去做,这个就是我们经常说的IOC(Inversion of Control,控制反转)。DI其实就是IOC计原则的一种实现。还有我们平常说的观察者默认,其实也是IOC的一种实现,核心就是把部分职责(非核心职责)转交出去,从而去构建出一种松耦合的应用!

什么是依赖注入容器(DI Container)?

DI Container ,也可以叫 IOC Container,其实是一个框架(Framework),它提供了一整套的DI解决方案,它负责创建依赖,然后自动把依赖注入到需要它们的其他对象里面,同时还负责管理依赖的生命周期!一些强大的第三方容器还提供各种各样的功能,使我们更加愉快的撸代码!

常用的第三方DI Container:

  • Spring.NET

  • Autofac

  • Unity

  • Ninject

ASP .NET Core中的DI

在ASP.NET Core中,把依赖统一称作服务(services),所以DI Container就也被称为Service Container,Asp.NET Core提供了一个简单的内置容器 IServiceProvider ,它默认支持构造器注入 constructor injection,满足我们大多数的功能需求!

服务主要分为两类:

  1. Framework Services:框架服务,由ASP.NET Core框架提供,例如 IApplicationBuilderIHostingEnvironment ... ,详情见https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#framework-provided-services

  2. Appliction Services:应用服务,由我们根据实际业务创建。

如果要通过DI容器自动实现注入我们的服务,我们必须要先在容器中登记服务(只需要注册应用服务,框架服务已经被ASP.NET Core框架注入了)。

注册服务(Registering Services)

假设我们有一个ILog接口和它的一个实现类,我们要把它注入到DI容器里面,然后在应用中使用它。

public interface ILog
{
void Info(string msg);
void Error(string err);
} public class ConsoleLogger:ILog
{
public void Info(string msg)
{
Console.WriteLine(msg);
}
public void Error(string err)
{
Console.WriteLine(err);
}
}

然后在Startup类的ConfigureServices()方法中注册上面的服务,ConfigureServices()有一个IServiceCollection参数,就是用它来注册应用服务。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Singleton));
}
}

ServiceDescriptor用来描述服务的类型、服务的具体实现已经服务的一个生命周期(Service Lifetime),上面我们指定了服务ILog的实现是ConsoleLogger,且是一个单例(Singleton)。

通常情况我们都是使用IServiceCollection扩展方法来注册服务

services.AddSingleton<ILog, ConsoleLogger>();//注册为单例

services.TryAddSingleton<ILog, ConsoleLogger>();

构造函数注入(Contractor Injection)

一旦我们注册了函数,当应用类的构造函数包含了需要依赖的服务,DI容器就自动帮我们注入依赖。

public class HomeController : Controller
{
ILog _log; public HomeController(ILog log)
{
_log = log;
}
public IActionResult Index()
{
_log.Info("Executing /home/index"); return View();
}
}    

控制器需要ILog服务,只需要在构造函数的参数中包含ILog类型即可,我们不需做其他任何事,DI容器自动给我们创建ILog的实例,并根据注入时指定的生命周期,在切当是时机销毁(Dispose)这个实例。

Action方法注入(Method Injection)

有时候,我们只需要在某一个方法中需要这个服务,这时,我们可以给方案的参数标记上[FromServices]    这个特性,容器就能自动为我们注入这个依赖服务的实例了

public IActionResult Index([FromServices] ILog log)
{ log.Info("Index method executing"); return View(); }

属性注入(Property Injection)

ASP.NET Core自带这个容器不支持,需要使用第三方容器,例如Autofac 。

服务的生命周期(Service Lifetime)

DI容器负责管理已注册服务的生命周期,它根据指定的生命周期自动销毁服务实例。ASP.NET Core 的服务可以配置以下三种生命周期形式:

  • Transient(瞬态)

    每次从DI容器中解析(获取)服务时,DI容器都是返回一个新的服务实例。

    //原始方法
    services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Transient));
    //扩展方法
    services.AddTransient<ILog, ConsoleLogger>();
  • Scoped(作用域)

    每一个作用域范围内(例如每一个HTTP 请求)从DI容器中解析出来的实例都是同一个

    //原始方法
    services.Add(new ServiceDescriptor(typeof(ILog), typeof(ConsoleLogger), ServiceLifetime.Scoped));
    //扩展方法
    services.AddScoped<ILog, ConsoleLogger>();
  • Singleton(单例)

    只在第一次请求是创建,之后所有请求都共享同一个服务实例,直到应用程序的生命周结束。所以在ASP.NET Core应用中,没有必须手动去创建一个单例,通过注册一个单例服务到容器中即可。

    //原始方法
    services.Add(new ServiceDescriptor(typeof(ILog), new ConsoleLogger()));
    //扩展方法
    services.AddSingleton<ILog, ConsoleLogger>();

DI使用经验的一些总结

泛型如何注册?

通过开放式泛型(Open Generics)注册服务。假设上面的类型修改成 ILog<T>

ConsoleLogger<T> ,那么我们按照下面的方式注册即可

services.AddScoped(typeof(ILog<>), typeof(ConsoleLogger<>));

相同的接口类型注入两个实现类型会怎样?

如果我们在注册服务时,相同类型注册多次并不会报错,但在解析时,返回的是最后一次注册的类型的实例。

    public interface ILog

    {
void Info(string msg);
} public class Logger1 : ILog
{
public void Info(string msg)
{
}
} public class Logger2 : ILog
{
public void Info(string msg)
{
}
} public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddTransient<ILog, Logger1>();
     services.AddTransient<ILog, Logger2>();
    }
}
public class HomeController : Controller
{
    ILog _log;
    public HomeController(ILog log)
    {     _log = log;//_log is Logger2      }
}

为了避免我们多次注册,导致具体实现被覆盖的问题,所以我们一般都是使用    TryAddTransient,这个方法注册时,检测到相同类型已经被注册过,就不会在进行注册

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
    services.TryAddTransient<ILog, Logger1>();
     services.TryAddTransient<ILog, Logger2>();
    }
}
public class HomeController : Controller
{
    ILog _log;
    public HomeController(ILog log)
    { _log = log;//_log is Logger1      }
}

自己想要手动从容器中获取服务对象,怎么做?

ASP.NET Core中的DI Container是IServiceProvider,只要获取这个对象,然后调用 GetService 这个方法即可。

  • 如果在Controller中

    HttpContext的属性RequestServices就是IServiceProvider类型,所以我们可以按照下面的方法:

          public IActionResult Index()
    {
    var services = this.HttpContext.RequestServices;
    var log = (ILog)services.GetService(typeof(ILog)); log.Info("Index method executing"); return View();
    }
  • 如果在应用中

    在应用中时,我们可以通过构造函数注入IServiceProvider

    public class MyAppService
    {
    private IServiceProvider _services;
    public MyAppService(IServiceProvider services)
    {
    _services=services;
    }
    public void Test()
    {
    //原始方法
    var log = (ILog)_services.GetService(typeof(ILog));
    //通过扩展方法,需要nuget添加Microsoft.Extensions.DependencyInjection.Abstractions 这个引用
    var log = _services.GetService<ILog>();
    }
    }

结语

ASP.NET Core已经很强大了,提供了很多实用功能,希望.Net的战友们能在基本知识储备的前提下,多多发掘总结出ASP.NET Core开发的最佳实践,为推动.NET Core生态建设贡献一份自己的力量!

浅谈ASP.NET Core中的DI的更多相关文章

  1. 浅谈ASP.NET Core中IOC与DI的理解和使用

    说起IOC和DI,使用过ASP.NET Core的人对这两个概念一定不陌生,早前,自己也有尝试过去了解这两个东西,但是一直觉得有点很难去理解,总觉得对其还是模糊不清,所以,趁着今天有空,就去把两个概念 ...

  2. 在Asp.Net Core中使用DI的方式使用Hangfire构建后台执行脚本

    最近项目中需要用到后台Job,原有在Windows中我们会使用命令行程序结合计划任务或者直接生成Windows Service,现在.Net Core跨平台了,虽然Linux下也有计划任务,但跟原有方 ...

  3. 浅谈 asp.net core web api

    希望通过本文能够了解如下内容: ControllerBase Attributes Action的返回值类型 ControllerBase 当我们开始实际上项目, 真正实操 anc 时, 肯定会用到 ...

  4. ASP.NET Core中的依赖注入(3): 服务的注册与提供

    在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...

  5. ASP.NET Core 中的依赖注入 [共7篇]

    一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...

  6. ASP.NET Core中的依赖注入(2):依赖注入(DI)

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...

  7. ASP.NET Core中的ActionFilter与DI

    一.简介 前几篇文章都是讲ASP.NET Core MVC中的依赖注入(DI)与扩展点的,也许大家都发现在ASP.NET CORE中所有的组件都是通过依赖注入来扩展的,而且面向一组功能就会有一组接口或 ...

  8. 【ASP.NET MVC系列】浅谈NuGet在VS中的运用

    一     概述 在我们讲解NuGet前,我们先来看看一个例子. 1.例子: 假设现在开发一套系统,其中前端框架我们选择Bootstrap,由于选择Bootstrap作为前端框架,因此,在项目中,我们 ...

  9. Asp.Net Core中DI的知识总结

    在asp.net core中DI的概念是由这几部分组成的: IServiceCollection,保存IServiceDescriptor实例的列表 IServiceProvider,只有一个方法Ge ...

随机推荐

  1. 吴裕雄--天生自然python学习笔记:Python3 XML 解析

    什么是 XML? XML 指可扩展标记语言(eXtensible Markup Language),标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. XML 被设计用来传输和存 ...

  2. VRRP笔记一:基本简介(注意iptables和selinux的问题)

    LAN客户端判定哪个路由器应该为其到达目标主机的下一跳网关的方式有动态及静态决策两种方式,其中,觉的动态路由发现方式有如下几种: 1.Proxy ARP —— 客户端使用ARP协议获取其想要到达的目标 ...

  3. Nginx for windows 访问路径包含中文

    转载自http://blog.csdn.net/five824/article/details/48261213 Nginx for windows 访问路径包含中文 原创 2015年09月07日 0 ...

  4. MyBatis XML 配置文件 properties 元素扩展

    在分析 MyBatis XML 配置文件 properties 元素时提到了三种配置方式,其中 property 子元素 和 properties 文件都比较容易理解,但是为什么还要提供一种代码参数传 ...

  5. spring+mybatis+mysql5.7实现读写分离,主从复制

    申明:请尽量与我本博文所有的软件版本保持一致,避免不必要的错误. 所用软件版本列表:MySQL 5.7spring5mybaties3.4.6 首先搭建一个完整的spring5+springMVC5+ ...

  6. 吴裕雄--天生自然 PYTHON语言数据分析:ESA的火星快车操作数据集分析

    import os import numpy as np import pandas as pd from datetime import datetime import matplotlib imp ...

  7. 为什么就连iPhone、三星手机的电池都能出问题?

    近年来关于三星.苹果.华为等知名手机厂商电池爆炸的消息一直不断在媒体上报道.这在一定程度上引发了消费者的重度忧虑,也给这些知名手机厂商从一定程度上造成了信任危机.为何连这些知名品牌都无法避免手机电池的 ...

  8. 【U创营学员招募】8节免费云计算课程,让你轻松掌握生产技能

    课程不错,免费听课.免费听课.免费听课,强烈推荐-! 公众号对话框回复"课程助手" 即可报名! ---END---

  9. 如何安装及使用honmaple社区程序 · honmaple's blog · 风落花语风落天,花落风雨花落田.

    Table of Contents 1. 如何安装及使用 1.1. 安装需要的package 1.2. 配置config 1.3. 注释下面代码 1.4. 初始化数据库 1.5. 创建管理员账户 2. ...

  10. hexo+github搭建自己的博客

    之前很早就想用hexo弄一个自己独立的博客了,在博客园也写了很多的博客,不过不喜欢博客园的风格.不过今天,终于折腾成功了,用hexo搭建了一个在github写的博客,开心,后面会将自己以前的博客慢慢迁 ...