什么是依赖注入

软件设计原则中有一个依赖倒置原则(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都会创建一个新的实例

做一个简单测试:

  1. 创建测试类:

    public interface ITest
    {
    Guid Guid { get; }
    } public class Test : ITest
    {
    public Guid Guid { get; } public Test()
    {
    Guid = Guid.NewGuid();
    }
    }
  2. 在ConfigureServices里注册

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddTransient<ITest, Test>();
    }
  3. 通过三种方法来获取这个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>
  4. 运行,结果如下

    03d437d6-2f18-452e-a7fd-ce62cea90381
    08b31487-b02b-4d62-bc2b-6d2026389f0c
    21a7fc13-6e7b-4590-910b-68d21a7a03d1

    说明三种方式获取了三个不同的实例, 刷新一下页面, 又变成了另外三个不同的值.

  5. 现在在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事务的功能。

  6. 再将 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>();
}
}

参考

ASP.NET Core 中的依赖注入的更多相关文章

  1. ASP.NET Core中的依赖注入(1):控制反转(IoC)

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

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

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

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

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

  4. ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理

    ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...

  5. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】

    本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...

  6. ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】

    通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最 ...

  7. ASP.NET Core中的依赖注入(5):ServicePrvider实现揭秘【补充漏掉的细节】

    到目前为止,我们定义的ServiceProvider已经实现了基本的服务提供和回收功能,但是依然漏掉了一些必需的细节特性.这些特性包括如何针对IServiceProvider接口提供一个Service ...

  8. ASP.NET Core 中的 依赖注入介绍

    ASP.NET Core 依赖注入 HomeController public class HomeController : Controller { private IStudentReposito ...

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

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

随机推荐

  1. postgresql的日志实现机制

    1.事务的概念   事务是从实际生活中引入数据库的一个概念,即事务内的操作,要么全做,要么全不做.就像银行转账一样,当从一个帐户转出一部分钱之后,就必须在另一个帐户中存入相同数目的钱,若是转出钱之后, ...

  2. 基于Extjs的web表单设计器 第一节

    前面一节介绍了表单设计器的背景和最终的大概样式,本节主要介绍表单设计器的需求及功能设计. 在讲需求之前先明确几个常用的概念: 主表或者卡片表——具有多行多列的一个区域的控件块,如下图所示. 明细表—— ...

  3. driver.get()和driver.navigate().to()到底有什么不同?-----Selenium快速入门(四)

    大家都知道,这两个方法都是跳转到指定的url地址,那么这两个方法有什么不同呢?遇到这种情况,第一反应就是查查官方的文档. 官方文档的说法是:Load a new web page in the cur ...

  4. 对Integer类中的私有IntegerCache缓存类的一点记录

    对Integer类中的私有IntegerCache缓存类的一点记录 // Integer类有内部缓存,存贮着-128 到 127. // 所以,每个使用这些数字的变量都指向同一个缓存数据 // 因此可 ...

  5. C/C++,python,java,C#月经贴问题

    在刚开始的时候,一直纠结于语言之争,什么什么有前途,什么什么没前途.对于什么的支持不好啦,个人信仰问题啦.什么都有. 首先最主要的一个个人观点:“语言不是老婆,不是一夫一妻制”.你可以同时拥有许多的女 ...

  6. “全栈2019”Java多线程第十六章:同步synchronized关键字详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. kvm快照备份及常用命令

    转载自:http://www.myjishu.com/?p=431 好文章 kvm快照备份及常用命令 kvm快照,分两种: 1种lvm快照,如果分区是lvm,可以利用lvm进行kvm的快照备份 2种由 ...

  8. WiFi安全那些事儿,整理推荐~

    即使你安装了防火墙,不连接任何WIFI和热点,不在任何不受信任的网站下载东西,及时清理缓存和个人敏感信息,你相信吗?你的个人隐私仍然可能被泄露出去! 基础篇: 推荐1  谁出卖了你  << ...

  9. Windows 计划任务之消息提醒

    Windows 计划任务之消息提醒 你肯定也有这种需求.想做一个计划任务,却发现老式消息提醒已经被微软禁止了. 或者就是很单纯的希望给系统弹出一个消息框而并非CMD的echo命令. so...how ...

  10. Linux nl --让输出的文件内容自动加上行号

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...