ASP.NET Core在底层内置了一个依赖注入框架,通过依赖注入的方式注册服务、提供服务。依赖注入不仅服务于ASP.NET Core自身,同时也是应用程序的服务提供者。

毫不夸张的说,ASP.NET Core通过依赖注入实现了各种服务对象的注册和创建,同时也实现了面向抽象的编程模式和编程体验,提升了应用程序的扩展性。

今天,我们普及一下ASP.NET Core中依赖注入的一些基本知识。

一、服务的注册

我们通过创建一个ASP.NET Core的项目,可以发现在Startup.cs 类中,有一个方法ConfigureServices,这个方法的注释是这样的:

     This method gets called by the runtime. Use this method to add services to the container.

在ConfigureServices方法中我们可以将通过ASP.NET Core内置的依赖注入框架实现服务的的注册。

这个方法有个参数:IServiceCollection,见名知意,服务集合。

ASP.NET Core内置的依赖注入框架将服务注册信息存储到一个实现了IServiceCollection接口的对象中。默认情况下这个接口的实现类是ServiceCollection,以下是这个类的说明:

https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollection?view=dotnet-plat-ext-3.1

通过这个接口和类实现,我们可以发现,注册服务其实就是将一个服务的ServiceDescriptor对象添加到ServiceCollection集合中。

例如:

public void ConfigureServices(IServiceCollection services)
{
services.Add(new ServiceDescriptor(typeof(IUserRepository), new UserRepository()));
services.AddControllers();
}

ServiceDescriptor可以理解为对某个服务注册项的描述。ASP.NET Core的依赖注入容器IServiceProvider通过ServiceDescriptor的信息,动态创建服务的实例Instance.

我们看一下这个ServiceDescriptor类:

有几个关键的属性:

1. ServiceType:服务的类型,例如服务接口的类型信息

2. ImplementationType:服务的实现类型,例如服务接口实现类的类型信息

3. ImplementationInstance:实现服务的实例,一般是服务单例模式场景下使用。 https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicedescriptor.-ctor?view=dotnet-plat-ext-3.1#Microsoft_Extensions_DependencyInjection_ServiceDescriptor__ctor_System_Type_System_Object_

4. Lifetime:服务生命周期:Scoped(同一个请求中同一个IServiceProvider提供的对象是同一个)、Singleton(单例)、Transient(每次从服务容器进行请求时创建)

5. ImplementationFactory 服务实例创建工厂,自定义的IServiceProvider服务提供容器

服务注册提供了一系列重载的方法,大家可以根据需要进行选择:

服务注册的过程中,涉及到了服务的生命周期的概念,接下来我们详细看一下。

二、服务生命周期

服务的生命周期设置,决定了服务提供容器IServiceProvider使用什么样的方式提供服务实例对象。正如上面第一章节所说的,

ASP.NET Core服务依赖注入框架,支持三种类型的服务生命周期:

  •    Singleton
  •    Scoped
  •    Transient

其中:

Transient:暂时的,每次从服务容器进行请求时创建。 这种生存期适合轻量级、 无状态的服务。

Singleton:单一实例,在第一次请求时(或者在运行 Startup.ConfigureServices 并且使用服务注册指定实例时)创建的。每个后续请求都使用相同的实例。

Scoped:范围内的,作用域生存期服务,以每个客户端请求(连接)一次的方式创建。可以这么理解:同一个请求中同一个IServiceProvider提供的对象是同一个。

微软给了个例子不错:先注册服务,三种类型

public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddScoped<IMyDependency, MyDependency>();
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty)); //OperationService depends on each of the other Operation types.
services.AddTransient<OperationService, OperationService>();
}

  第一个请求:

控制器操作:
暂时性:d233e165-f417-469b-a866-1cf1935d2518
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000 OperationService 操作:
暂时性:c6b049eb-1318-4e31-90f1-eb2dd849ff64
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

  第二个请求:

第二个请求:
控制器操作:
暂时性:b63bd538-0a37-4ff1-90ba-081c5138dda0
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000 OperationService 操作:
暂时性:c4cbacb8-36a2-436d-81c8-8c1b78808aaf
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

大家可以根据实际的需要选择服务的生命周期,创建不同类型的服务。

三、服务的消费

前面,我们将服务注册到IServiceCollection,ASP.NET Core服务提供容器IServiceProvider就可以根据IServiceCollection 创建具体类型的服务对象了。

我们先看一下IServiceProvider接口,可以发现:只有一个GetService方法。

我们可以通过以下代码使用:

 public static void Main(string[] args)
{
var builder = CreateHostBuilder(args);
var host = builder.Build(); var userRepo = host.Services.GetService(typeof(IUserRepository)) as IUserRepository;
userRepo.AddUser("user"); host.Run();
}

同时,我们更多常用的是:

将服务通过ASP.NET Core依赖注入框架注入到控制器中

ASP.NET Core MVC 控制器通过构造函数显式请求依赖关系。即:通过构造函数注入服务的实现。

前面,我们通过ConfigureServices注册了服务IUserRepository,在Controller这一层如何消费使用这个服务呢?答案就是在Controller构造函数中注入。

看一段示例代码:(HomeController的构造函数中,增加了一个参数IUserRepository)

  public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private IUserRepository _userRepository; public HomeController(ILogger<HomeController> logger, IUserRepository userRepository)
{
_logger = logger;
_userRepository = userRepository;
} public IActionResult Index()
{
_userRepository.AddUser(new User() { }); return View();
} public IActionResult Privacy()
{
return View();
} [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}

  同时,ASP.NET Core MVC 控制器支持通过注解FromServicesAttribute, 将服务直接注入到Action方法中,而无需使用构造函数注入:

public IActionResult Index([FromServices] IUserRepository userRepository)
{
userRepository.AddUser(new User() { }); return View();
}

  ASP.NET Core除了支持将服务注入到控制器,同时还支持将服务依赖注入到视图,可以参考以下链接:

https://docs.microsoft.com/zh-cn/aspnet/core/mvc/views/dependency-injection?view=aspnetcore-3.0

以上是对ASP.NET Core依赖注入框架的研究,分享给大家。

周国庆

2020/4/12

ASP.NET Core技术研究-探秘依赖注入框架的更多相关文章

  1. ASP.NET Core技术研究-探秘Host主机启动过程

    当我们将原有ASP.NET 应用程序升级迁移到ASP.NET Core之后,我们发现代码工程中多了两个类Program类和Startup类. 接下来我们详细探秘一下通用主机Host的启动过程. 一.P ...

  2. ASP.NET Core中如影随形的”依赖注入”[下]: 历数依赖注入的N种玩法

    在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程层面.在ASP.NET ...

  3. Asp.Net Core 3.1学习-依赖注入、服务生命周期(6)

    1.前言 面向对象设计(OOD)里有一个重要的思想就是依赖倒置原则(DIP),并由该原则牵引出依赖注入(DI).控制反转(IOC)及其容器等概念.在学习Core依赖注入.服务生命周期之前,下面让我们先 ...

  4. ASP.NET Core中如影随形的”依赖注入”[上]: 从两个不同的ServiceProvider说起

    我们一致在说 ASP.NET Core广泛地使用到了依赖注入,通过前面两个系列的介绍,相信读者朋友已经体会到了这一点.由于前面两章已经涵盖了依赖注入在管道构建过程中以及管道在处理请求过程的应用,但是内 ...

  5. ASP.NET Core技术研究-全面认识Web服务器Kestrel

    因为IIS不支持跨平台的原因,我们在升级到ASP.NET Core后,会接触到一个新的Web服务器Kestrel.相信大家刚接触这个Kestrel时,会有各种各样的疑问. 今天我们全面认识一下ASP. ...

  6. 基础教程:视图中的ASP.NET Core 2.0 MVC依赖注入

    问题 如何在ASP.NET Core MVC Views中注入和使用服务. 解 更新 启动 类来为MVC添加服务和中间件. 添加一项服务 添加一个Controller,返回 ViewResult. 添 ...

  7. Asp.Net Core 3.0的依赖注入改变

    Asp.Net Core 3.0出来很久了,预览版的时候就被我偶像Lemon大人,带着尝试摸索了一下这个 那么Asp.Net Core 3.0和Asp.Net Core 2.X到底有哪些区别呢? As ...

  8. Asp.net core 学习笔记 ( DI 依赖注入 )

    比起 Angular 的依赖注入, core 的相对简单许多, 容易明白 所有 provider 都在 startup 里配置. public void ConfigureServices(IServ ...

  9. ASP.NET Core 过滤器中使用依赖注入

    如何给过滤器ActionFilterAttribute也用上构造函数注入呢? 一般自定义的过滤器直接用特性方式标识就能使用 [ContentFilter] 因为构造函数在使用的时候要求传参,然后我们可 ...

随机推荐

  1. 如何优雅的使用AbpSettings

    在Abp中配置虽然使用方便,但是每个配置要先定义key,要去provider中定义,再最后使用key从ISetting中获取还是挺麻烦的一件事, 最主要是获取修改的时候,比如,修改用户配置,是从获取一 ...

  2. vue-autoui自匹配webapi的UI控件

    vue-autoui 是一款基于vue和element扩展的一个自动化UI控件,它主要提供两个控件封装分别是auto-form和auto-grid; 通过这两个控件可以完成大多数的信息输入和查询输出的 ...

  3. 使用 Pandas 的 to_excel() 方法来将多个 csv 文件合并到一个 xlsx 的不同 sheets 内

    这几天在用 Python3 研究一个爬虫,最后一个需求是把爬下来的20+个csv文件整合到一个excel表里的不同sheets. 初版的核心代码如下: while year <= 2018: c ...

  4. Git&Github入门

    Github: 仓库repository: 存放项目代码,每个项目对应一个项目 收藏star: 收藏 复制克隆项目(Fork): 发起请求Pull Reques: 别人改进你的代码,如果觉得不错可以合 ...

  5. Who Gets the Most Candies? POJ - 2886(线段树单点更新+区间查询+反素数)

    预备知识:反素数解析 思路:有了反素数的解法之后就是线段树的事了. 我们可以用线段树来维护哪些人被淘汰,哪些人没被淘汰,被淘汰的人的位置,没被淘汰的人的位置. 我们可以把所有人表示为一个[1,n]的区 ...

  6. 开源项目OEIP 游戏引擎与音视频多媒体(UE4/Unity3D)

    现开源一个项目 OEIP 项目实现的功能Demo展示 这个项目演示了在UE4中,接入摄像机通过OEIP直接输出到UE4纹理上,并直接把UE4里的RenderTarget当做输入源通过OEIP里GPU管 ...

  7. python报错:ERROR: No matching distribution found for dns.resolver

    可能有的小伙伴在安装dns.resolver的时候会遇到这个问题: 我百度的时候别人是: pip install dns-python 但是我这样安装也还是错误.有些时候是这个包改名了所以你没有搜索到 ...

  8. mybatis3.2.7 原理和入门程序

    使用jdbc操作数据库有以下缺点   |--数据库连接,使用时就创建,不使用立即释放,对数据库进行频繁开启和关闭,造成数据源资源浪费,影响数据库性能.    设想:使用数据库连接池管理数据库连接.   ...

  9. webapck学习笔记

    该笔记是博主学习webpack课程时做的笔记,里面加了一些自己的一些理解,也踩了一些坑,在笔记中基本上都更正过来了,分享给大家,如果发现什么问题,望告知,非常感谢. 1. 为什么要学webapck 为 ...

  10. IDEA编写 Scala时没有语法检查和错误提示

    #问题描述 在IDEA中写scala程序时,一些明显的语法错误不进行检查,没有像写Java时的红线什么的,当编译运行时,编译失败. https://blog.csdn.net/weizheng1000 ...