上篇文章《在.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/