ASP.NET Core依赖注入多个服务实现类
依赖注入在 ASP.NET Core 中起中很重要的作用,也是一种高大上的编程思想,它的总体原则就是:俺要啥,你就给俺送啥过来。
服务类型的实例转由容器自动管理,无需我们在代码中显式处理。
因此,有了依赖注入后,你的编程思维就得变一变了。
在过去,许多功能性的类型(比如一个加密解密的类),我们都喜欢将其定义为静态(static),而有了依赖注入,你就要避免使用静态类型,应该交由服务容器帮你管理,只要你用好了,你会发现依赖注入是很方便的。
依赖注入的初级玩法,也是比较标准的玩法,此种玩法有两种模式:
1、十代单传模式:一个接口对应一个类,比如先定义接口 IA、IB,随后,类A实现 IA,类B 实现 IB。一对一。也可以是抽象类(或基类)E,然后 F 继承 E 类。
2、断子绝孙模式:直接就写一个类,不考虑派生,直接就添加到服务容器中。
来,看个例子。
我先定义个接口。
public interface IPlayGame
{
void Play();
}
然后,写一个类来实现它。
public class NBPlayGame : IPlayGame
{
public void Play()
{
Console.WriteLine("全民打麻药。");
}
}
我们知道,所谓服务类,其实就是普通类,这些类一般用于完成某些功能,比如计算 MD5 值。接着呢,还记得 Startup 类有个 ConfigureServices 方法吧,对,就在这厮里面把我们刚刚那个服务进行注册(就是添加到 ServiceCollection 集合中)。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IPlayGame, NBPlayGame>();
}
添加的时候很简单,类型一对一,IPlayGame 接口与 NBPlayGame 类对应。添加时有三种方法你可以调用,实际上对应着,服务类在容器中的生命周期。
AddSingleton:单个实例,这是寿命最长的,与天同寿。整个应用程序中仅用一个实例。
AddTransient:这个是最短命的,可能是天天晚上加班熬夜,死得很快。此种情况下,服务类的实例是用的时候创建,用完后直接销毁。
AddScoped:这个比较难理解。它的生命周期在单个请求内,包括客户端与服务器之间随后产生的子请求,反正只要请求的会话结束了,就会清理。
后,你就可以进行注入了,比如在中间件,在控制器,或者在其他服务类的构造函数上(中间件是在 Invoke / InvokeAsync 方法上)进行实例接收。
现在来用一下,写一个中间件。
public class TestMiddleware
{
public TestMiddleware(RequestDelegate next) { } public Task InvokeAsync(HttpContext context, IPlayGame game)
{
game.Play();
return Task.CompletedTask;
}
}
已注册的服务会注入到 InvokeAsync 方法的参数中。注意第一个参数是 HttpContext,这是必须参数,后面的是注入的参数。
最后,在 Startup 类的 Configure 方法中就可以 use 这个中间件了。
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<TestMiddleware>();
}
运行后,Play 方法调用,在控制台中输出以下结果。

“断子绝孙”模式,不使用接口规范,直接写功能类。
public class DoSomething
{
public string GetMessage() => "你好,刚才 Boss 找你。";
}
注册服务时更简单。
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<DoSomething>();
}
在 Configure 方法中进行注入。
public void Configure(IApplicationBuilder app, DoSomething thing)
{
Console.WriteLine(thing.GetMessage());
}
运行后,输出结果如下。

在容器中,使用 ServiceDescriptor 类来存储服务类型相关的信息。
其中,ServiceType 表示的是服务的类型,如果服务是有接口与实现类的,那么这个属性指的是接口的类型,实现类的类型信息由 ImplementationType 属性存储。
如果没有接口,直接只定义类型,那么这个类型的信息就存到 ServiceType 属性上,ImplementationType 属性不使用。
上面这些例子中,ServiceType 是 IPlayGame 接口相关信息,ImplementationType 是 NBPlayGame 类的信息。
如果像上面 DoSomething 类的情况,则 ServiceType 为 DoSomething 相关的信息,ImplementationType 为空。
接下来,咱们看高级玩法。
定义一个接口。
public interface IDemoService
{
string Version { get; }
void Run();
}
然后,有两个类实现这个接口。
public class DemoService1 : IDemoService
{
public string Version => "v1";
public void Run()
{
Console.WriteLine("第一个服务实现类。");
}
}
public class DemoService2 : IDemoService
{
public string Version => "v2"; public void Run()
{
Console.WriteLine("第二个服务实现类。");
}
}
然后,我们注册服务。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IDemoService, DemoService1>();
services.AddTransient<IDemoService, DemoService2>();
}
然后我们照例,接收注入,咱们依旧使用中间件的方法参数接收。
public class DemoMiddleware
{
public DemoMiddleware(RequestDelegate next)
{
// 由于程序约定,此构造函数必须提供。
}
public async Task InvokeAsync(HttpContext context, IDemoService sv)
{
await context.Response.WriteAsync(sv.Version);
}
}
然后,在 Startup.Configure 方法中使用该中间件。
public void Configure(IApplicationBuilder app, DoSomething thing)
{
app.UseMiddleware<DemoMiddleware>();
}
运行之后,你发现问题了,看看输出
参数仅能接收到最后注册的实现类型实例,也就是 DemoService2 类。
所以就看到网上有不少朋友发贴问了,.NET Core 是不是不支持多个服务实现类的注入?这难倒了很多人。
方法一、接收 IServiceProvider 类型的注入。
public async Task InvokeAsync(HttpContext context, IServiceProvider provider)
{
StringBuilder sb = new StringBuilder();
foreach (var sv in provider.GetServices<IDemoService>())
{
sb.Append($"{sv.Version}<br/>");
}
await context.Response.WriteAsync(sb.ToString());
}
只要能接收到 IServiceProvider 所引用的实例,就能通过 GetServices 方法获取多个服务实例。
方法二,更简单,直接注入 IEnumerable<T> 类型,本例中就是 IEnumerable<IDemoService>。
public async Task InvokeAsync(HttpContext context, IEnumerable<IDemoService> svs)
{
StringBuilder sb = new StringBuilder();
foreach (var sv in svs)
{
sb.Append($"{sv.Version}<br/>");
}
await context.Response.WriteAsync(sb.ToString());
}
Enumerable<T> 的妙处就是可以 foreach ,这样你也能访问多个实例,而且必要时还可以联合 LINQ 一起耍。
运行结果如下。

ASP.NET Core依赖注入多个服务实现类的更多相关文章
- ASP.NET Core依赖注入系统学习教程:关于服务注册使用到的方法
在.NET Core的依赖注入框架中,服务注册的信息将会被封装成ServiceDescriptor对象,而这些对象都会存储在IServiceCollection接口类型表示的集合中,另外,IServi ...
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- 实现BUG自动检测 - ASP.NET Core依赖注入
我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...
- ASP.NET Core 依赖注入最佳实践——提示与技巧
在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...
- ASP.NET Core依赖注入最佳实践,提示&技巧
分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...
- ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】
ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...
随机推荐
- tansition-group 使用方法
<transition-group name="breadcrumb"> <el-breadcrumb-item v-for="(item,index) ...
- QEMU支持的网络模式
网络是现代计算机系统不可或缺的一部分,QEMU也对虚拟机提供丰富的网络支持.qemu-kvm中主要给客户机提供了如下4种不同模式的网络. (1)基于网桥(Bridge)的虚拟网卡 (2)基于NAT(N ...
- CSS清除浮动方法集合
CSS清除浮动方法集合 一.浮动产生原因 - TOP 一般浮动是什么情况呢?一般是一个盒子里使用了CSS float浮动属性,导致父级对象盒子不能被撑开,这样CSS float浮动就产生了. ...
- git pull There is no tracking information for the current branch.
在高版本的 git下面,也许会看见这样的提示: 解决方案:指定当前工作目录工作分支,跟远程的仓库,分支之间的链接关系. 比如我们设置master对应远程仓库的master分支 git branch - ...
- 禁止select标签选择,禁止select组件change值
大家知道, 对于HTML控件select, 是没有readOnly属性的,所以设置它并不起作用,如: 如果用disabled的话,提交时又取不到值. 那有什么方法可以实现?可以有一个折中的方法,如下: ...
- zabbix使用fping监控任意两个节点之间的网络质量、丢包率和响应时间
zabbix使用fping监控任意两个节点之间的网络质量.丢包率和响应时间 之前的博文 使用zabbix3..4的ICMP Ping模版实现对客户端网络状态的监控 https://www.cnblog ...
- shell编程系列5--数学运算
shell编程系列5--数学运算 方法1 expr $num1 operator $num2 方法2 $(($num1 operator $num2)) expr操作符对照表1 操作符 含义 num1 ...
- SeetaFace2 cmake VS2015编译编译
cmake Selecting Windows SDK version 10.0.17134.0 to target Windows 10.0.18362. == BUILD_VERSION: v2. ...
- Qt连接数据库
Qt连接数据库,参数设置 //连接数据库 bool VCManageDatabase::connectMYSQL() { //判断testConnect连接是否存在并连接 if (QSqlDataba ...
- Qt编写气体安全管理系统17-记录清理
一.前言 记录清理功能,在数据量很小的情况下,用不上,如果数据量大了的话,长年累月存储的,那就显得极其重要了,好比视频监控中的NVR存储的视频一样,一般来说存储个60天,那超过60天怎办呢,擦除早期的 ...