ASP.NET Core中GetService()和GetRequiredService()之间的区别
上篇文章《在.NET Core 3.0中的WPF中使用IOC图文教程》中,我们尝试在WPF中应用.NET Core
内置的IOC进行编程,在解析MainWindow
的时候我用了GetRequiredService<T>()
方法,当时就在想这个GetRequiredService<T>()
方法跟GetService<T>()
到底有什么区别呢,于是乎,谷歌了一把,就发现了一篇文章来介绍他们区别的,于是乎尝试翻译一把,希望对大家有所帮助。文章最后会给出原文链接,以下就是翻译内容:
作者:依乐祝
原文地址:https://www.cnblogs.com/yilezhu/p/11107648.html
本文将介绍Microsoft.Extensions.DependencyInjection
中提供的默认/内置ASP.NET Core DI容器的方法GetService<T>()
和GetRequiredService<T>()
方法。我将描述它们之间的差异以及您应该使用哪种方法。
如果服务不存在则
GetService()
返回null
,GetRequiredService()
而是抛出异常。如果您正在使用第三方容器,请尽可能使用GetRequiredService
- 如果发生异常,第三方容器可能就会根据异常信息提供相应的诊断信息,以便您可以找出未注册预期服务的原因。
容器的核心 - IServiceProvider接口
ASP.NET Core依赖注入抽象的核心是IServiceProvider
接口。该接口实际上是System
命名空间中基类库的一部分。接口本身很简单:
public interface IServiceProvider
{
object GetService(Type serviceType);
}
一旦您使用DI容器(使用IServiceCollection
)注册了所有类,几乎所有DI容器需要做的就是允许您使用GetService()
查找对象的实例。
当然,您通常根本不应该直接在代码中使用IServiceProvider
。相反,您应该使用标准的构造函数注入,并让框架来承载并在幕后使用IServiceProvider
。
直接使用
IServiceProvider
是服务定位器模式的一个示例。这通常被认为是反模式,因为它隐藏了类的依赖关系。
然而,有些时候你没有选择的余地。例如,如果您试图将服务注入到属性,或者在配置DI容器时使用“转发”类型,则需要直接使用IServiceProvider
。
比较GetService ()和GetRequiredService ()
鉴于我们不再使用.NET 1.0,如果你想从IServiceProvider
中检索服务,你可能使用了通用的泛型GetService<T>()
扩展方法,而不是GetService(Type)
接口方法。但是你可能也注意到了类似的GetRequiredService<T>()
扩展方法 - 问题是,它们之间有什么区别呢,您应该使用哪种方法?
在我们研究任何代码之前,让我们先讨论一下这些方法的预期行为。首先,从GetService()
方法的文档开始:
GetService()
返回一个serviceType
类型的服务对象。如果返回的是一个没有类型的服务对象serviceType
则返回null
。
与GetRequiredService()
的文档内容进行对比:
GetRequiredService()
返回一个serviceType
类型的服务对象。如果没有serviceType
类型的服务,则抛出一个InvalidOperationException
异常。
因此,当请求的实例serviceType
可用时,两种方法的行为都相同。不同之处在于serviceType
未注册时的行为:
GetService
- 如果服务未注册,则返回null
GetRequiredService
- 如果服务未注册,则抛出一个Exception
异常。
现在我们已经清楚了,让我们看看一些代码。
在ServiceProviderServiceExtensions
班上Microsoft.Extensions.DependencyInjection.Abstractions库中同时实现了通用版GetService<T>()
和GetRequiredService<T>()
方法,如下所示:
我已经从本文的代码中删除了一些前提条件检查; 如果你想看到完整的代码,请在GitHub上查看。
public static class ServiceProviderServiceExtensions
{
public static T GetService<T>(this IServiceProvider provider)
{
return (T)provider.GetService(typeof(T));
}
public static T GetRequiredService<T>(this IServiceProvider provider)
{
return (T)provider.GetRequiredService(typeof(T));
}
}
这两种方法实际上都是相同的 - 通用扩展方法委托给非泛型版本的GetService()
和GetRequiredService()
。它们只是一种便利,因此您在自己的代码中不需要使用更多的typeof()
和类型转换。
非泛型版本的GetService()
是IServiceProvider
接口的一部分,但非泛型GetRequiredService()
实现是同一类中的扩展方法:
public static class ServiceProviderServiceExtensions
{
public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
{
var requiredServiceSupportingProvider = provider as ISupportRequiredService;
if (requiredServiceSupportingProvider != null)
{
return requiredServiceSupportingProvider.GetRequiredService(serviceType);
}
var service = provider.GetService(serviceType);
if (service == null)
{
throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
}
return service;
}
}
该方法的第一步是检查提供的IServiceProvider
是否也实现了ISupportRequiredService
。此接口提供底层的非泛型GetRequiredService
实现,因此如果服务提供者实现它,GetRequiredService()
则可以直接调用。
ASP.NET Core内置的DI容器并没有实现
ISupportRequiredService
- 只有第三方容器实现了GetRequiredService()
。
如果IServiceProvider
没有实现ISupportRequiredService
,则执行所需的异常抛出行为,如您所料:GetService()
调用,如果返回null
则抛出异常。
那你应该使用哪种方法?
正如我之前所说,理想情况下,两者都可以!
在您自己的代码使用ISeviceProvider
通常是你正在使用服务定位器反模式的一个标志,所以一般应避免使用ISeviceProvider
。但是,如果由于设计限制而需要(例如,您不能在属性中使用DI),或者作为DI容器配置本身的一部分的情况下,您应该使用哪一种呢?
基于GitHub中要求添加GetRequiredService()
的原始问题,以及Jeremy D. Miller先前提出的问题 ,我认为几乎所有情况下的规则是:
使用
GetRequiredService()
- 减少重复。如果服务不可用,则使用
GetRequiredService()
会立即抛出异常。如果您使用GetService()
,那么您需要在调用代码中检查是否为null
,并且通常需要抛出异常。那个空检查代码需要在任何地方重复。 - 失败很快。如果您在使用
GetService()
时忘记检查是否为null
,那么稍后您的程序可能会以NullReferenceException
结束。找出导致异常的原因总是比显式的告诉你的InvalidOperationException
更困难,需要做更多的工作。 - 允许对第三方容器进行高级诊断。StructureMap和其他一些第三方容器的一大好处是,它们能够提供详细的异常消息,说明为什么找不到服务。如果您正在使用
GetRequiredService()
,则第三方容器本身会生成异常,因此可以提供其他特定于容器的信息。只返回null
(带GetService()
)不会给你进一步的详细的信息。这是引入GetRequiredService()
的主要原因。
当然,我已经看到了一些反对GetRequiredService()`的观点,但我认为其中任何一个都不会受到审查:
- “我没有使用第三方容器”。如果您正在使用内置容器(未实现
ISupportRequiredService
),那么您将无法通过使用任何其他诊断获益GetRequiredService()
。但是,我认为前两个优势仍然存在,并使GetRequiredService
值得使用。此外,如果您以后添加第三方容器,您已经在使用最佳实践了。 - “我有可选服务,有时只在DI容器中注册。” 。这可能是使用
GetService()
唯一有效的理由。如果您的代码只有在注册了给定服务时才能运行,那么您可能需要使用GetService()
。但是,如果GetService()
返回NULL,我也看到它在使用回退服务时使用。在我看来,这很少是应用程序代码的好模式。回退的编排应该是DI容器配置的一部分,而不是使用服务的位置。
所以,现在你有了 - GetService()
与GetRequiredService()
之间的对比了。在我进一步挖掘它之前,当我选择一个而不是另一个时,我有点武断,但现在我会确保我总是理所当然的使用GetRequiredService()
。
摘要
GetService()
是IServiceProvider
上的唯一方法,ISeviceProvider
是ASP.NET核心DI抽象中的中央接口。第三方容器还可以实现可选接口ISupportRequiredService
,该接口提供GetRequiredService()
方法。当请求的类型serviceType
可用时,这些方法的行为相同。如果服务不可用(即它没有注册),则GetService()
返回null
,而GetRequiredService()
抛出一个InvalidOperationException
。
GetRequiredService()
相对于GetService()
的主要好处是当服务不可用时,它允许第三方容器提供额外的诊断信息。因此,在使用第三方容器时最好使用GetRequiredService()
。就个人而言,我会在任何地方使用它,即使我只使用内置的DI容器。
原英文链接:https://andrewlock.net/the-difference-between-getservice-and-getrquiredservice-in-asp-net-core/
ASP.NET Core中GetService()和GetRequiredService()之间的区别的更多相关文章
- Asp.Net Core中服务的生命周期选项区别和用法
在做一个小的Demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,寻找一番,发现是配置依赖注入服务时,对于 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- 在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度
在这篇文章中,我将介绍如何使用ASP.NET Core托管服务运行Quartz.NET作业.这样的好处是我们可以在应用程序启动和停止时很方便的来控制我们的Job的运行状态.接下来我将演示如何创建一个简 ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【总体设计 】
本系列前面的文章我们主要以编程的角度对ASP.NET Core的依赖注入系统进行了详细的介绍,如果读者朋友们对这些内容具有深刻的理解,我相信你们已经可以正确是使用这些与依赖注入相关的API了.如果你还 ...
- 在ASP.NET Core中怎么使用HttpContext.Current
一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目 ...
- ASP.NET Core中的ActionFilter与DI
一.简介 前几篇文章都是讲ASP.NET Core MVC中的依赖注入(DI)与扩展点的,也许大家都发现在ASP.NET CORE中所有的组件都是通过依赖注入来扩展的,而且面向一组功能就会有一组接口或 ...
- ASP.NET Core中使用GraphQL - 第八章 在GraphQL中处理一对多关系
ASP.NET Core中使用GraphQL - 目录 ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间 ...
随机推荐
- Linux(Centos7)下自动启动程序
1.文件转移 先将要执行的文件转移或复制到路径较短的地方如:/usr/local 主要是为了方便,同时防止误删.2.编写Service文件 $ vim /usr/lib/systemd/system/ ...
- Method and apparatus for training a memory signal via an error signal of a memory
Described herein is a method and an apparatus for training a memory signal via an error signal of a ...
- C#常用多线程方法
1. Thread类 C#多线程编程中Thread类需要包含名称空间System.Threading. class Program { static void Main(string[] args) ...
- 隐变量模型(latent variable model)
连续隐变量模型(continuous latent model)也常常被称为降维(dimensionality reduction) PCA Factor Analysis ICA 连续的情形比离散的 ...
- silverlight,WPF动画终极攻略之白云飘,坐车去旅游篇(Blend 4开发)
原文:silverlight,WPF动画终极攻略之白云飘,坐车去旅游篇(Blend 4开发) 这章有点长,所以我分成了两章.这一章主要是准备工作,差不多算美工篇吧,这章基本不会介绍多少动画效果,主要讲 ...
- matlab 二元函数的画法
plot:画线(curve,二维空间以及三维空间) surf:画面(surface,一般在三维空间) 1. surf 绘图函数 surf 是 surface 的缩写,表示表面(显然至少三维图像才会有表 ...
- ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 用户注册 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 用户注册 上一章节我们终于迁移完了 Identity 的数据,也创建 ...
- CCNP路由实验之七 动态路由之BGP
CCNP路由实验之七 动态路由之BGP 动态路由协议能够自己主动的发现远程网络,仅仅要网络拓扑结构发生了变化,路由器就会相互交换路由信息,不仅能够自己主动获知新添加的网络,还能够在当前网络连接失 ...
- VC++ 编译libcurl 支持SSL,GZIP(有脚本)
由于网上下载的 libcurl 不支持 gzip,只好自己动手编译,期间走了很多弯路,下面是最终成功的记录. 我所使用的环境 Visual Studio 2010 . Windows 7 64 bit ...
- Java Swing编程接口(30)---列表框:JList
列表框同时可以在信息呈现给用户的列表多个选项,使用JList能够建立一个列表框. package com.beyole.util; import java.awt.Container; import ...