.NET 8 升級踩坑 - ASP.NET Core DI 多建構式設定失效
0 |
.NET 8.0 已於 11/14 發佈,.NET 8 為 LTS (長期支援版,單數版號如 .NET 7 為 STS,支援週期只有 18 個月 ) 較符合企業應用需求。最近便試著將 .NET 6.0 專案升到 .NET 8.0,原以為可無痛升級,但連續踩坑搞到灰頭土臉,這裡先分享多建構式型別的 DI 冷門問題一枚。
我的專案有個型別有多個建構式,在使用 DI (依賴注入) 時,DI 服務會不知道使用哪個建構式,這個議題之前研究過,簡單做法是在預期 DI 採用的建構式加上 [ActivatorUtilitiesConstructor]
,註冊物件時寫成 builder.Services.AddSingleton<MyService>(sp => ActivatorUtilities.CreateInstance<MyService>(sp));
。
這個做法在 .NET 6 運行多時,試著將專案升級到 .NET 8 後,它開了第一槍,冒出 'Multiple constructors for type 'MyService' were found with length 2.'
錯誤:
我寫了一段小程式重現問題:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<Foo>();
builder.Services.AddSingleton<Bar>();
builder.Services.AddSingleton<MyService>(sp => ActivatorUtilities.CreateInstance<MyService>(sp));
var app = builder.Build();
Console.WriteLine($".NET {Environment.Version}");
try
{
var scope = app.Services.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<MyService>();
Console.WriteLine("MyService created successfully");
}
catch (Exception ex)
{
Console.WriteLine("MyService failed to create");
Console.WriteLine(ex.Message);
}
public class Foo { }
public class Bar { }
public class MyService
{
[ActivatorUtilitiesConstructor]
public MyService(Foo foo, IWebHostEnvironment env) { }
public MyService(Bar bar, IWebHostEnvironment env) { }
}
將 csproj <TargetFramework>net8.0</TargetFramework>
改成 6.0 或 7.0 都能正常執行,切到 8.0 才會出錯。
追了一下原始碼找到問題根源,.NET 8 為了改善 ActivatorUtilities.CreateInstance() 效能,合併了一個修改,原本 bool isPreferred = constructor.IsDefined(typeof(ActivatorUtilitiesConstructorActtribute))
的檢查被移除,導致行為改變。
在 .NET Runtime 發了 Issue,得到回應是這屬於 .NET 8 的行為變更(Breaking Change),ActivatorUtilities.CreateInstance() 改為會尋找參數最多的建構式,若有個數相同的建構式便會發生錯誤,而 ActivatorUtilitiesConstructorActtribute 已失去其原有作用。
在上述程式為第一個建構式多加參數,即可引導 ActivatorUtilities.CreateInstance() 使用它建構物件:
增加第二個建構式參數則會改用其建立物件,證明 [ActivatorUtilitiesConstructor] 已無作用。
若專案原本依賴 ActivatorUtilitiesConstructorActtribute 決定建構式,升級 .NET 8 需要進行調整。
ActivatorUtilitiesConstructorAttribute can help ActivatorUtilities.CreateInstance() to choose the preferred constructor of mutilple-contructor type. It works fine in .NET 6/7, and failed in .NET 8.
Comments
Be the first to post a comment