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

上篇文章《在.NET Core 3.0中的WPF中使用IOC圖文教程》中,咱們嘗試在WPF中應用.NET Core內置的IOC進行編程,在解析MainWindow的時候我用了GetRequiredService<T>()方法,當時就在想這個GetRequiredService<T>()方法跟GetService<T>()到底有什麼區別呢,因而乎,谷歌了一把,就發現了一篇文章來介紹他們區別的,因而乎嘗試翻譯一把,但願對你們有所幫助。文章最後會給出原文連接,如下就是翻譯內容:html

做者:依樂祝
原文地址:http://www.javashuo.com/article/p-vrbcckrr-kg.htmlgit


本文將介紹Microsoft.Extensions.DependencyInjection中提供的默認/內置ASP.NET Core DI容器的方法GetService<T>()GetRequiredService<T>()方法。我將描述它們之間的差別以及您應該使用哪一種方法。github

若是服務不存在則GetService()返回nullGetRequiredService()而是拋出異常。若是您正在使用第三方容器,請儘量使用GetRequiredService- 若是發生異常,第三方容器可能就會根據異常信息提供相應的診斷信息,以便您能夠找出未註冊預期服務的緣由編程

容器的核心 - IServiceProvider接口

ASP.NET Core依賴注入抽象的核心是IServiceProvider接口。該接口其實是System命名空間中基類庫的一部分。接口自己很簡單:框架

public interface IServiceProvider
{
    object GetService(Type serviceType);
}

一旦您使用DI容器(使用IServiceCollection)註冊了全部類,幾乎全部DI容器須要作的就是容許您使用GetService()查找對象的實例。ide

固然,您一般根本不該該直接在代碼中使用IServiceProvider。相反,您應該使用標準的構造函數注入,並讓框架來承載並在幕後使用IServiceProvider函數

直接使用IServiceProvider是服務定位器模式的一個示例。這一般被認爲是反模式,由於它隱藏了類的依賴關係。ui

然而,有些時候你沒有選擇的餘地。例如,若是您試圖將服務注入到屬性,或者在配置DI容器時使用「轉發」類型,則須要直接使用IServiceProviderthis

比較GetService ()和GetRequiredService ()

鑑於咱們再也不使用.NET 1.0,若是你想從IServiceProvider中檢索服務,你可能使用了通用的泛型GetService<T>()擴展方法,而不是GetService(Type)接口方法。可是你可能也注意到了相似的GetRequiredService<T>()擴展方法 - 問題是,它們之間有什麼區別呢,您應該使用哪一種方法?.net

在咱們研究任何代碼以前,讓咱們先討論一下這些方法的預期行爲。首先,從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/

相關文章
相關標籤/搜索