如果您使用了.NET Core,则很可能已使用Microsoft.Extensions.DependencyInjection中的内置依赖项注入容器,在本文中,我想更深入地了解Microsoft Dependency Injection(DI)容器中的 IServiceCollection。

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

Microsoft依赖项注入容器只是一组类,它们组合到一个代码库中,这个库会自动创建并管理程序中需要的对象。

我们先看下面的代码:

public class ClassA
{public void DoWork() {var b = new ClassB();b.DoStuff();}
}public class ClassB
{public void DoStuff(){// ...}
}

ClassA直接依赖ClassB,并且在它的DoWork方法中,new了一个ClassB,然后调用了ClassB的DoStuff方法。

我们改写一下代码看看:

public class ClassA
{private readonly ClassB _dependency;public ClassA(ClassB classB) => _dependency = classB;public void DoWork() => _dependency.DoStuff();
}public class ClassB : IThing
{public void DoStuff(){// ...}
}

首先,我加了一个构造函数,并且指定了ClassA依赖的类型,调用构造函数时,必须提供ClassB的实例, 在ClassA的内部,我们不会去new一个ClassB,ClassB完全是由外部传入的,这里就是控制反转(IoC)。

进一步改进代码:

public interface IThing
{public void DoStuff();
}public class ClassA
{private readonly IThing _dependency;public ClassA(IThing thing) => _dependency = thing;public void DoWork() => _dependency.DoStuff();
}public class ClassB : IThing
{public void DoStuff(){// ...}
}

加了一个接口IThing,现在,我们已经应用了SOLID的依赖倒置原则,我们不再依赖具体的实现,相反,我们依赖于IThing抽象,在构造函数中,只需要传入IThing的实现就好了。

然后在我们的代码中,可以这样用:

class Program
{static void Main(string[] args){IThing thing = new ClassB();ClassA classA = new ClassA(thing);classA.DoWork();}
}

我们手动new了一个ClassB,它实现了IThing接口,然后创建ClassA的时候,直接把thing传入构造函数中。

上面的代码演示,我们只处理了ClassA和ClassB的依赖注入关系,但是在实际中呢,我们代码中有很多类型,然后有各种各样的依赖关系。

这个时候我们就需要一个DI容器,我们对容器进行配置,然它知道什么类型,然后负责自动创建并管理对象(通常称为服务)。

注册服务

通常, Microsoft DI 容器需要在Startup类中配置,在这里,您可以使用ConfigureServices方法向容器注册服务,在应用程序托管生命周期的早期,将调用ConfigureServices方法,它有一个参数IServiceCollection,这个参数在初始化应用程序时传入。

public class Startup
{public void ConfigureServices(IServiceCollection services){// 注册服务}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){}
}

为了尽可能的简单,我们也可以在控制台中使用 Microsoft DependencyInjection。

创建控制台程序后,我们首先在项目中引入Microsoft.Extensions.DependencyInjection

<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net5.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /></ItemGroup></Project>

现在我们开始注册我们的服务,但是我们需要一个IServiceCollection,让我们看一下IServiceCollection的定义。

public interface IServiceCollection : IList<ServiceDescriptor>
{
}

IServiceCollection没有定义其任何成员,而是从IList<ServiceDescriptor>派生。

Microsoft.Extensions.DepenencyInjection程序包里面,它有一个默认的实现:ServiceCollection。

public class ServiceCollection : IServiceCollection
{private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();public int Count => _descriptors.Count;public bool IsReadOnly => false;public ServiceDescriptor this[int index]{get{return _descriptors[index];}set{_descriptors[index] = value;}}// ...
}

它有一个私有的List集合:_descriptors,里面是ServiceDescriptor。

让我们从创建一个ServiceCollection,然后注册两个服务。

static void Main(string[] args)
{var serviceCollection = new ServiceCollection();serviceCollection.AddSingleton<ClassA>();serviceCollection.AddSingleton<IThing, ClassB>();Console.WriteLine("Done");
}

在前面的代码中,我们已经使用AddSingleton方法注册了两个服务,这不是IServiceCollection接口定义的方法,也不在ServiceCollection上,这是IServiceCollection的扩展方法,这个方法在ServiceCollectionServiceExtensions的扩展类中,接下来,我会介绍这个方法是如何注册服务的,不过这之前,我们首先回顾下服务生命周期的概念。

服务生命周期

在Microsoft依赖项注入框架中,我们可以使用三种生命周期注册服务,分别是单例(Singleton)、瞬时(Transient)、作用域(Scoped),在上面的代码中,我使用了AddSingleton()来注册服务。

使用Singleton服务的优点是我们不会创建多个服务实例,只会创建一个实例,保存到DI容器中,直到程序退出,这不仅效率高,而且性能高,但是有一个要注意的点,如果在多线程中使用了Singleton,要考虑线程安全的问题,保证它不会有冲突。

瞬时(Transient)和单例(Singleton)模式是相反的,每次使用时,DI容器都是创建一个新的实例。

作用域(Scoped),在一个作用域内,会使用同一个实例,像EF Core的DbContext上下文就被注册为作用域服务。

我们注册服务时会发生什么?

在上面的代码中,我已经注册了两个单例服务。

serviceCollection.AddSingleton<ClassA>();
serviceCollection.AddSingleton<IThing, ClassB>();

这是最终的AddSingleton方法:

public static IServiceCollection AddSingleton(this IServiceCollection services,Type serviceType,Type implementationType)
{if (services == null){throw new ArgumentNullException(nameof(services));}if (serviceType == null){throw new ArgumentNullException(nameof(serviceType));}if (implementationType == null){throw new ArgumentNullException(nameof(implementationType));}return Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
}

我们可以看到AddSingleton方法调用了私有的Add方法,并且传入了一个生命周期的枚举值ServiceLifetime.Singleton

让我们看一下Add方法的工作原理:

private static IServiceCollection Add(IServiceCollection collection,Type serviceType,Type implementationType,ServiceLifetime lifetime)
{var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);collection.Add(descriptor);return collection;
}

它创建一个新的ServiceDescriptor实例,传入服务类型,实现类型(可能与服务类型相同)和生命周期,然后调用Add方法添加到列表中。

之前,我们了解到IServiceCollection本质上是包装了List <ServiceDescriptor>, ServiceDescriptor类很简单,代表一个注册的服务,包括其服务类型,实现类型和生命周期。

实例注册

我们也可以手动new一个实例,然后传入到AddSingleton()方法中:

var myInstance = new ClassB();
serviceCollection.AddSingleton<IThing>(myInstance);

使用 ServiceDescriptor

我们还可以手动定义一个ServiceDescriptor,然后直接添加到IServiceCollection中。

var descriptor = new ServiceDescriptor(typeof(IThing), typeof(ClassB), ServiceLifetime.Singleton);
serviceCollection.Add(descriptor);

总结

在本文中,介绍了.NET中的DI的一些核心知识,可以直接创建ServiceCollection来使用Microsoft DI框架,了解了IServiceCollection上的AddSingleton扩展方法是如何工作,以及它们最终创建了一个ServiceDescriptor,然后添加到一个ServiceCollection包装的List集合中。

原文链接: https://www.stevejgordon.co.uk/aspnet-core-dependency-injection-what-is-the-iservicecollection

欢迎扫码关注我们的公众号 【全球技术精选】,专注国外优秀博客的翻译和开源项目分享,也可以添加QQ群 897216102

探索 .NET Core 依赖注入的 IServiceCollection相关推荐

  1. 探索 .NET Core 依赖注入的 IServiceProvider

    在上一篇文章中,我们学习了Microsoft.Extensions.DependencyInjection中的IServiceCollection,包括服务注册转换为ServiceDescriptor ...

  2. 深入理解ASP.NET Core依赖注入

    概述 ASP.NET Core可以说是处处皆注入,本文从基础角度理解一下原生DI容器,及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器. 什么是依赖注入 百度百科中对于依赖注入的定义:控制反转( ...

  3. ASP.NET Core依赖注入深入讨论

    这篇文章我们来深入探讨ASP.NET Core.MVC Core中的依赖注入,我们将示范几乎所有可能的操作把依赖项注入到组件中. 依赖注入是ASP.NET Core的核心,它能让您应用程序中的组件增强 ...

  4. ASP.NET Core依赖注入容器中的动态服务注册

    介绍 在ASP.NET Core中,每当我们将服务作为依赖项注入时,都必须将此服务注册到ASP.NET Core依赖项注入容器.但是,一个接一个地注册服务不仅繁琐且耗时,而且容易出错.因此,在这里,我 ...

  5. .net core依赖注入

    .net core依赖注入小结 #依赖注入介绍 .net core 中依赖注入是必不可少的一项必学内容,官方介绍为:依赖注入是一种软件设计模式,指一个或多个依赖(或服务)被注入,或通过引用传递,传入一 ...

  6. .Net Core 依赖注入

    .Net Core 依赖注入 为什么要使用依赖注入框架 .Net Core DI 核心类 三种生命周期 服务注册 单例注册 作用域注册 瞬时注册 直接注入实例 工厂模式注册 注册不同实例 尝试注册 移 ...

  7. everythingtoolbar.dll”或它的一个依赖项。_ASP.NET Core依赖注入最佳实践、提示和技巧...

    译者前言 本文译自ABP框架的开发博客<ASP.NET Core Dependency Injection Best Practices, Tips & Tricks>一文(原作者 ...

  8. ASP.NET Core依赖注入最佳实践,提示技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  9. 【ASP.NET Core】ASP.NET Core 依赖注入

    一.什么是依赖注入(Denpendency Injection) 这也是个老身常谈的问题,到底依赖注入是什么? 为什么要用它? 初学者特别容易对控制反转IOC(Iversion of Control) ...

最新文章

  1. Elasticsearch - cluster_block_exception [FORBIDDEN/12/index read-only / allow delete (api)]
  2. 蓝桥杯-用宏求球的体积(java)
  3. 原理 快速邻近匹配_论文推荐 | 陈晓勇:低空摄影测量立体影像匹配的现状与展望...
  4. 惠普m1005连接电脑步骤_电脑连接电视机详细步骤方法图文
  5. RUNOOB python练习题30 回文数
  6. TensorFlow 2.0 - Keras Pipeline、自定义Layer、Loss、Metric
  7. Pyinstaller打包Django项目
  8. Qt工作笔记-发送端发送Json格式的数据包,接收端解析数据包
  9. lua和unity如何交互_(XLua)C#与Lua中的交互
  10. linux rdma测试,硬件RDMA的驱动配置和测试
  11. Scala学习笔记(二)表达式和函数
  12. 智能判断图片中是否存在某物体_如果给猫披上象皮,神经网络将作何判断?
  13. java容器输入_Java 容器初识篇
  14. 【渝粤题库】陕西师范大学210024 学前儿童科学教育 作业 (专升本)
  15. 上市企业高管背景特质研究解析
  16. slack 时钟_【基础知识】时序(Slack、Setup、Hold、Jitter、Skew、亚稳态)
  17. 直播网站html代码,怎么生成嵌入网页直播的HTML代码
  18. 唐玄奘:不要因为走得太远,而忘了为什么出发
  19. 使用Snakemake搭建分析流程
  20. NetSpeed--网速检测

热门文章

  1. 应用安全-操作系统安全-漏洞修复整理
  2. Pycharm下将py文件打包成exe文件
  3. Java发送邮件(带附件)
  4. 2018-10-03-Python全栈开发-day60-django序列化-part3
  5. 你的知识死角不能否定你的技术能力
  6. wampServer配置WWW根目录遇到的坑
  7. AR Software
  8. apt 根据注解,编译时生成代码
  9. System.FormatException: GUID 应包含带 4 个短划线的 32 位数(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)。...
  10. maven 多项目搭建