最近又在專案中碰到需要將原本單實現的介面改造成多個實現的場景,這裡記錄一下常見的幾種改法。
假設已經存在如下介面ICustomService
和其實現CustomService
,由於只有一種實現,注入和使用非常容易。
public interface ICustomService
{
void MethodA();
void MethodB();
}
public class CustomService: ICustomService
{
public void MethodA()
{
}
public void MethodB()
{
}
}
//注入
builder.Services.AddTransient<ICustomService, CustomService>();
//使用
private readonly ICustomService _customService;
public CustomController(ICustomService customService)
{
_customService = customService;
}
現在我們需要增加一種實現。
我們可以將原ICustomService
內的方法移到到一個新的基介面,共用出來,需要多少個實現,就建立多少個空介面繼承該基介面。
//基介面
public interface ICustomBaseService
{
void MethodA();
void MethodB();
}
//多個空介面
public interface ICustomService : ICustomBaseService
{
}
public interface ICustomServiceV2 : ICustomBaseService
{
}
//第一種實現
public class CustomService: ICustomService
{
public void MethodA()
{
}
public void MethodB()
{
}
}
//第二種實現
public class CustomServiceV2: ICustomServiceV2
{
public void MethodA()
{
}
public void MethodB()
{
}
}
//注入
builder.Services.AddTransient<ICustomService, CustomService>();
builder.Services.AddTransient<ICustomServiceV2, CustomServiceV2>();
//使用
private readonly ICustomService _customService;
private readonly ICustomServiceV2 _customServiceV2;
public CustomController(ICustomService customService,ICustomServiceV2 customServiceV2)
{
_customService = customService;
_customServiceV2 = customServiceV2;
}
這種實現方式需要增加了一套空介面做隔離,看似可能比較「浪費」,但後期隨著專案的演進,ICustomService
和ICustomServiceV2
可能會慢慢分化,我們可以很方便的為它們擴充各種獨有方法。
如果我們確定不要要多個介面,也可以使用下面的單介面實現
public interface ICustomService
{
void MethodA();
void MethodB();
}
//第一種實現
public class CustomService: ICustomService
{
public void MethodA()
{
}
public void MethodB()
{
}
}
//第二種實現
public class CustomServiceV2: ICustomService
{
public void MethodA()
{
}
public void MethodB()
{
}
}
//注入
builder.Services.AddTransient<ICustomService, CustomService>();
builder.Services.AddTransient<ICustomService, CustomServiceV2>();
//使用
private readonly ICustomService _customService;
private readonly ICustomServiceV2 _customServiceV2;
public CustomController(IEnumerable<ICustomService> customServices)
{
_customService = customServices.ElementAt(0);
_customServiceV2 = customServices.ElementAt(1);
}
從上面程式碼可以看到,我們是為從介面ICustomService
註冊兩個實現,並從IEnumerable<ICustomService>
解析出了這兩個實現。這裡可能會有兩個疑問
CustomService
沒有被第二個實現CustomServiceV2
替換掉?IEnumerable<ICustomService>
解析到我們需要的服務?答案在Microsoft.Extensions.DependencyInjection.ServiceDescriptor 和 Microsoft.Extensions.DependencyInjection.ServiceCollection 這兩個類裡,程序裡,依賴注入的服務,會被新增到ServiceCollection
裡,ServiceCollection
是一組ServiceDescriptor
的集合,ServiceDescriptor
通過服務型別、實現以及生命週期三個組合在一起構成的標識來確定服務。而ICustomService+CustomService+Transient
和ICustomService+CustomServiceV2+Transient
是兩個不同的ServiceDescriptor
,因此不會被替換。同時服務型別的ServiceDescriptor
會被聚合在一起,於是我們可以很方便的從IEnumerable物件中解析出所有的同型別的服務。
本質上,兩種方法都是多型性(Polymorphism)的應用,沒有優劣之分,根據場景選擇合適的寫法。
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection
https://github.com/dotnet/runtime
作者: 幾秋
出處: https://www.cnblogs.com/netry/p/net-dependency-injection-multiple-implementations.html
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。