ASP.NET Core 中的依赖注入
什么是依赖注入
软件设计原则中有一个依赖倒置原则(DIP),为了更好的解耦,讲究要依赖于抽象,不要依赖于具体。而控制反转(Ioc)就是这样的原则的其中一个实现思路, 这个思路的其中一种实现方式就是依赖注入(DI)。
什么是依赖:当一个类需要另一个类协作来完成工作的时候就产生了依赖。
什么是注入: 注入体现的是一个IOC(控制反转的的思想)。正转是自己来实例化需要的依赖。反转是类不应该自己创建它,而是应该由它的调用者传给它。于是可以通过构造函数等让外界把依赖传给类。
为什么要反转 为了在业务变化的时候尽少改动代码可能造成的问题。基于抽象添加新的实现。只需要在原来注入的地方改一下就可以了。
什么是容器 容器统一管理系统中的所有依赖。容器负责两件事情:
- 绑定服务与实例之间的映射关系
- 获取实例并对实例进行管理(创建与销毁)
ASP .NET Core 中使用依赖注入
- IServiceCollection 负责注册服务,是一个IList类型的集合。
- IServiceProvider 负责提供实例,是由IServiceCollection的扩展方法BuildServiceProvider创建的。
- ServiceDescriptor 单个服务描述
- Type ServiceType: 服务的类型
- Type ImplementationType: 实现的类型
- ServiceLifetime Lifetime: 服务的生命周期
- object ImplementationInstance: 实现服务的实例
- Func<IServiceProvider, object> ImplementationFactory: 创建服务实例的工厂
注册
ServiceCollection提供了三种注册方法分别对应着三种实例生命周期。
- AddSingleton 整个应用程序生命周期以内只创建一个实例
- AddScoped 在同一个Scope内只初始化一个实例,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
- AddTransient 每一次GetService都会创建一个新的实例
做一个简单测试:
创建测试类:
public interface ITest
{
Guid Guid { get; }
} public class Test : ITest
{
public Guid Guid { get; } public Test()
{
Guid = Guid.NewGuid();
}
}
在ConfigureServices里注册
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ITest, Test>();
}
通过三种方法来获取这个Test类实例, Controller和View中代码如下
public class HomeController : Controller
{
private ITest _test;
private ILogger<HomeController> _logger; public HomeController(ITest test, ILogger<HomeController> logger)
{
this._test = test;
this._logger = logger;
} public IActionResult Index()
{
//通过构造函数获取
var res1 = this._test;
ViewBag.TestFromConstructor = res1; //通过HttpContext获取
var res2 = HttpContext.RequestServices.GetService<ITest>();
ViewBag.TestFromContext = res2; return View();
}
}
@inject ITest TestFromView
<ul>
<li>@ViewBag.TestFromConstructor.Guid</li>
<li>@ViewBag.TestFromContext.Guid</li>
<li>@TestFromView.Guid</li>
</ul>
运行,结果如下
03d437d6-2f18-452e-a7fd-ce62cea90381
08b31487-b02b-4d62-bc2b-6d2026389f0c
21a7fc13-6e7b-4590-910b-68d21a7a03d1
说明三种方式获取了三个不同的实例, 刷新一下页面, 又变成了另外三个不同的值.
现在在startup文件中将原来的 services.AddTransient<ITest,Test>() 改为 services.AddScoped<ITest,Test>() , 其他不变, 重新运行一下, 结果如下
050fef7e-2dc3-4d7d-8733-683b54b40b0b
050fef7e-2dc3-4d7d-8733-683b54b40b0b
050fef7e-2dc3-4d7d-8733-683b54b40b0b
刷新一下:
c9e5df8d-b085-4e3a-b883-fa083ba1d136
c9e5df8d-b085-4e3a-b883-fa083ba1d136
c9e5df8d-b085-4e3a-b883-fa083ba1d136
三组数字相同, 刷新一下, 又变成了另外三组一样的值, 这说明在同一次请求里, 获取的实例是同一个。
最常用的DBContext默认构建为Scope实例。即能减少实例初始化的消耗,还能实现跨Service事务的功能。
再将 services.AddScoped<ITest,Test>() 改为 services.AddSingleton<ITest,Test>() , 重新运行, 这次结果是
42ef5162-5781-427b-ac9d-a152500ed32f
42ef5162-5781-427b-ac9d-a152500ed32f
42ef5162-5781-427b-ac9d-a152500ed32f
发现三组值是一样的, 说明获得的是同一个实例, 在刷新一下页面, 仍然是这三组值, 说明多次请求获得的结果也是同一个实例.
使用
在Startup类ConfigureService中初始化
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddTransient<ITest, Test>();
}
方法中默认调用了services.AddMvc(), 是IServiceCollection的一个扩展方法 public static IMvcBuilder AddMvc(this IServiceCollection services), 作用就是向这个清单中添加了一些MVC需要的服务,例如Authorization、RazorViewEngin、DataAnnotations等。
Controller中使用
private ITest _test;
private ILogger<HomeController> _logger; public HomeController(ITest test, ILogger<HomeController> logger)
{
this._test = test;
this._logger = logger;
}
通过HttpContext来获取实例
HttpContext下有一个RequestedService同样可以用来获取实例对象,不过这种方法一般不推荐。需要添加Microsoft.Extension.DependencyInjection的using来调用这个方法的。HttpContext.RequestServices.GetService<ITest>()
View中使用
在View中通过@inject声明@inject ITest TestFromView <ul>
<li>@TestFromView.Guid</li>
</ul>
释放
对于每次请求, 我们最初配置的根IServiceProvider通过CreateScope()创建了一个新的IServiceScope, 而这个IServiceScope的ServiceProvider属性将负责本次该次请求的服务提供, 当请求结束, 这个ServiceProvider的dispose会被调用。
在2.0中, ServiceProvider只调用由它创建的 IDisposable 类型的 Dispose。 如果将一个实例添加到容器,它将不会被释放。
例如:
services.AddSingleton<ITest>(new Test());
替换为其它的 Ioc 容器
可以将默认的容器改为其他的容器, 比如Autofac, 需要把Startup类里面的 ConfigureService的 返回值从 void改为 IServiceProvider即可。而返回的则是一个AutofacServiceProvider。
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add other framework services
// Add Autofac
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
}
}
参考
- Dependency injection in ASP.NET Core
- 全面理解 ASP.NET Core 依赖注入
- ASP.NET Core 2.0 : 六. 举个例子来聊聊它的依赖注入
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core 中的依赖注入的更多相关文章
- ASP.NET Core中的依赖注入(1):控制反转(IoC)
ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了"标准化&qu ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】
通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最 ...
- ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】
到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个Service ...
- ASP.NET Core 中的 依赖注入介绍
ASP.NET Core 依赖注入 HomeController public class HomeController : Controller { private IStudentReposito ...
- ASP.NET Core 中的依赖注入 [共7篇]
一.控制反转(IoC) ASP.NET Core在启动以及后续针对每个请求的处理过程中的各个环节都需要相应的组件提供相应的服务,为了方便对这些组件进行定制,ASP.NET通过定义接口的方式对它们进行了 ...
随机推荐
- CSS Animation triggers text rendering change in Safari
薄荷新首页上周五内测,花哥反馈在 MacBook Safari 浏览器下 鼠标移动到第一个商品的时候后面几个商品的文字会加粗.这是什么鬼??? 待我回到家打开笔记本,鼠标蹭蹭蹭的发现问题远不止如此: ...
- TFS文件编码检查机制和修改(Team Foundation Server 2013)
TFS的版本控制系统会自动按照下面的标准检测代码文件的编码格式: 1. 首先,如果代码文件包含了BOM部分,则使用BOM中制定的编码格式打开文档 什么是BOM (Byte order mark)? h ...
- 如何修改TFS 2013中工作项附件大小限制
默认情况下,TFS工作项的附件大小限制为4MB.我们可以通过调用TFS提供的Web Service将这个限制调整最高到2GB. 调整这个设置的必备条件是你需要拥有TFS应用层管理员的权限.下面来看看如 ...
- 执行计划--WHERE条件的先后顺序对执行计划的影响
在编写SQL时,会建议将选择性高(过滤数据多)的条件放到WHERE条件的前面,这是为了让查询优化器优先考虑这些条件,减少生成最优(或相对最优)的执行计划的时间,但最终的执行计划生成过滤顺序还是决定这些 ...
- shell相关文件
站在用户登录的角度来说,SHELL的类型: 登录式shell: 正常通常某终端登录 su - USERNAME su -l USERNAME 非登录式shell: su USERNAME 图形终端下 ...
- vsftp -samba-autofs
摘要: 1.FTP文件传输协议,PAM可插拔认证模块,TFTP简单文件传输协议. 注意:iptables防火墙管理工具默认禁止了FTP传输协议的端口号 2.vsftpd服务程序三种认证模式?三种认证模 ...
- 爬取lol皮肤
#!/usr/bin/python # -*- coding: utf-8 -*- # data:2018-11-23 # user:fei import re import requests imp ...
- 多个音频audio2
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【BZOJ2084】【洛谷P3501】[POI2010]ANT-Antisymmetry(Manache算法)
题意描述 原题: 一句话描述:对于一个0/1序列,求出其中异或意义下回文的子串数量. 题解 我们可以看出,这个其实是一个对于异或意义下的回文子串数量的统计,什么是异或意义下呢?平常,我们对回文的定义是 ...
- 老调重弹-access注入过主机卫
本文作者:i春秋签约作家——非主流 大家好,我是来自农村的非主流,今天就给在座的各位表演个绝活. 首先打开服务器上安装了主机卫士的网站. 尝试在变量id的值后面插入万恶的单引号,根据报错,我们可以分析 ...