通過Microsoft.Extensions.DependencyInjection,我能否一次性解決型別并構造一個實體,同時提供額外的建構式引數?
我想做的事情很容易通過例子來說明。下面,在 CreateSomethingWithContext 中,我需要使用 ActivatorUtilities.CreateInstance 來呼叫一個引數化的建構式,但是我并沒有事先知道 ISomething 的具體型別。
我可以使用 serviceProvider.GetRequiredService 來決議型別并創建一個默認的實體,但是這樣我就無法傳遞 context 引數。
我可以使用工廠版的AddTransient,但這將破壞已經使用GetRequiredService來創建默認實體的代碼。
下面,一個可能的笨辦法(我不喜歡)是創建一個多余的 Something 的默認實體,只是為了弄清它的型別并將它與 ActivatorUtilities.CreateInstance 一起傳遞給 context 引數。
是否有更好的方法?澄清一下,我無法控制實作ISomething/Something的實際庫。
using System;
using Microsoft.Extensions.DependencyInjection;
namespace App
{
public interface ISomething
{
void DoSomething() => Console.WriteLine(nameof(DoSomething) )。
}
public class Something: ISomething: ISomething.
{
public Something() =>
Console.WriteLine($"{this.GetType().Name} created");
public Something(object) =>
Console.WriteLine($"{this.GetType().Name}用{context}"創建。)
}
static class Program
{
private static IServiceProvider BuildServices() => new ServiceCollection()
.AddTransient<ISomething, Something>()
.BuildServiceProvider()。
static ISomething CreateSomething(IServiceProvider serviceProvider) =>
serviceProvider.GetRequiredService<ISomething>()。
static ISomething CreateSomethingWithContext(IServiceProvider serviceProvider, object context) 。
{
//首先我需要一個ISomething的實體,只需要了解它的具體型別即可。
var something = serviceProvider.GetRequiredService<ISomething>()。
var type = something.GetType();
//現在我有了這個型別,我可以使用ActivatorUtilities.CreateInstance。
var something2 = (ISomething)ActivatorUtilities.CreateInstance(
serviceProvider, type, context)。)
return something2;
}
static void Main()
{
var serviceProvider = BuildServices()。
CreateSomething(serviceProvider)。
CreateSomethingWithContext(serviceProvider, Guid.NewGuid())。
}
}
}
uj5u.com熱心網友回復:
就個人而言,我同意你的觀點,不希望用ActivatorUtilities.CreateInstance來做這件事--雖然它可以作業,但如果建構式的簽名發生變化,你最終會得到一個運行時錯誤,而我確信一個編譯時錯誤會更受歡迎。
為什么不注冊一個完全獨立的工廠,以便用背景關系構建您的物件 - 不要觸及 AddTransient() 注冊,這樣所有依賴默認建構式的呼叫代碼仍將作業,并在您需要插入背景關系時使用新建的工廠?
uj5u.com熱心網友回復:
你所要求的東西從根本上違背了依賴性反轉原則的思想,而這正是依賴性注入的基礎。因此,你不可能在常見的依賴注入容器中找到對這樣的東西的支持。
為了理解這一點,讓我們想一想為什么你實際上將你的具體實作Something注冊為其介面型別ISomething。通過這樣做,你允許組件依賴抽象而不是具體型別。這降低了耦合度,因為它允許您輕松地將實作與滿足介面規范的其他東西進行交換。
我們的想法是,當您依賴ISomething時,您實際上并不想知道您得到的實作是什么,只要它與介面兼容。這也意味著您不想知道該具體實作本身可能有哪些依賴性。相反,被依賴的組件將控制權交給了外部系統,通常是 DI 容器,并且只期望該系統能夠給你提供你所需要的東西。
現在,當把這個想法與ActivatorUtilities.CreateInstance相比較時,人們可能會注意到這個實用方法實際上只是圍繞服務定位器模式的語法糖,這通常與依賴注入直接相反。你現在可以主動向容器本身請求你的依賴性,而不是依靠系統給你提供你所依賴的東西。ActivatorUtiltilies.CreateInstance就是這樣做的,同時還允許您傳遞不應該從容器中決議的值。
讓我們假設存在一種方法,允許你進行 CreateInstance<ISomething>(extraValues)。你現在要求容器為你提供 ISomething 的實作,但顯然你對它的具體實作非常了解,以至于你能夠知道為了解決這個問題,你要傳遞哪些引數。而這與我上面解釋的相矛盾,即你通常(故意)不想知道具體的實作是什么。
相反,我建議你為此創建一個適當的工廠,它可以為你創建那個
Something,并提供一個適當的型別來添加 "背景關系 "資料。
public class SomethingFactory : ISomethingFactory
{
private readonly SomeDependency _someDependency;
public SomethingFactory(SomeDependency someDependency)
{
_someDependency = someDependency。
}
public ISomething Create()
=> new Something(_someDependency)。
public ISomething CreateContext(object)
=> new Something(_someDependency, context);
}
該工廠是為Something的實作而制作的,所以它完全可以知道需要向Something建構式傳遞什么樣的引數,而且由于它本身有一個抽象的ISomethingFactory,所以你仍然能夠使用依賴注入而不是使用服務定位器模式正確地完成這個任務。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311761.html
標籤:
