ASP.NET Core中GetService()和GetRequiredService()之間的區別

来源:https://www.cnblogs.com/yilezhu/archive/2019/07/01/11107648.html
-Advertisement-
Play Games

上篇文章《 "在.NET Core 3.0中的WPF中使用IOC圖文教程" 》中,我們嘗試在WPF中應用 內置的IOC進行編程,在解析 的時候我用了 方法,當時就在想這個 方法跟 到底有什麼區別呢,於是乎,谷歌了一把,就發現了一篇文章來介紹他們區別的,於是乎嘗試翻譯一把,希望對大家有所幫助。文章最後 ...


上篇文章《在.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()返回nullGetRequiredService()而是拋出異常。如果您正在使用第三方容器,請儘可能使用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/


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 依賴註入的概念 當一個對象要調用另一個對象時,一般是new一個被調用的對象,示例: class A{ private B b=new B(); public void test(){ b.say(); } } A類的對象依賴於B類對象,如果沒有B類對象,A類對象就不能正常工作,稱A依賴於B。 以上方 ...
  • 外觀模式:為子系統中的一組介面提供一個一致的界面,該模式定義了一個高層介面,這個介面使這個子系統更加容易使用。 我們先看一個使用外觀模式的例子,首先定義了三個子系統類 使用外觀模式定義了一個高層介面,這個介面定義了調用子系統的方法 介面調用子系統的具體實現 使用外觀模式的調用方法 執行結果: met ...
  • 上一篇學習了多線程的一些基礎知識:多線程的基本概念,及創建和操作多線程。內容相對簡單,但多線程的知識肯定不會這麼簡單,否則我們也不需要花這麼多心思去學習,因為多線程中容易出現線程安全問題。 那麼什麼是線程安全呢,定義如下: 當多個線程訪問同一個對象時,如果不用考慮這些線程在運行時環境下的調度和交替運 ...
  • django 中間件 [TOC] 自定義中間件 中間件可以定義五個方法,分別是:(主要的是process_request和process_response) process_request(self,request) process_view(self, request, view_func, vi ...
  • MyBatis 類型處理器(typeHandlers)簡介及源碼分析 ...
  • 新聞 "逐漸演化的.NET Core框架" "Visual Studio提示與技巧" "Windows Termina(預覽)" "Microsoft在GitHub上的工程師從2000名增加至25000名" 視頻及幻燈片 "實用IRL" "Visual Studio的F 工具解析" "事件溯源DIY ...
  • 1)ThreadLocal如何回收value,什麼時候回收?從ThreadLocal中的內部類分析:① ThreadLocalMap是一個定製的哈希映射,僅適用於維護線程本地值。為了幫助處理非常大和長期使用的用法,哈希表條目使用weakreferences作為鍵。但是,由於不使用引用隊列,因此只有當 ...
  • 一、註解(Annotation) 1、什麼是註解? 從JDK5開始,Java增加了Annotation(註解),Annotation是代碼里的特殊標記,這些標記可以在編譯、類載入、運行時被讀取,並執行相應的處理。 2、Annotation與註釋的區別: (1)Annotation不是程式本身,可以對 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...