在使用 Autofac 框架進行開發後,編寫整合測試時,需要用 Mock 的用於測試的模擬的型別去代替容器裡面已注入的實際型別,也就需要在 Autofac 完全收集完成之後,再次注入模擬的物件進行覆蓋原有業務程式碼註冊的正式物件。但 Autofac 預設沒有提供此機制,我閱讀了 Autofac 的原始碼之後,建立了一些輔助程式碼,實現了此功能。本文將告訴大家如何在整合測試裡面,在使用了 Autofac 的專案裡面,在所有收集完成之後,注入用於測試的 Mock 型別,和 Autofac 接入的原理
背景
為什麼選擇使用 Autofac 框架?原因是在此前的 WPF 專案裡面,有使用過的是 MEF 和 Autofac 兩個框架,而 MEF 的效能比較糟心。解決 MEF 效能問題的是 VS-MEF 框架。在後續開發的一個 ASP.NET Core 專案裡面,也就自然選用了 Autofac 框架
對比原生的 ASP.NET Core 自帶的 DI 框架,使用 Autofac 的優勢在於支援模組化的初始化,支援屬性注入
預設的 Autofac 可以通過 Autofac.Extensions.DependencyInjection
將 Autofac 和 dotnet 通用依賴注入框架合入在一起,但在 Autofac 裡面的定製要求是在 Startup 的 ConfigureContainer 函式裡面進行依賴注入,也就是在預設的 ASP.NET Core 裡面沒有提供更靠後的依賴注入方法,可以在完成收集之後,再次注入測試所需要的型別,覆蓋業務程式碼裡面的實際物件
需求
假定在一個應用,如 ASP.NET Core 應用裡面,進行整合測試,想要在整合測試裡面,使用專案裡面的依賴注入關係,只是將部分型別替換為測試專案裡面的模擬的型別
而在應用裡面,實際的業務型別是在 Autofac 的 Module 進行注入的。在應用裡面的大概邏輯如下,在 Program 的 CreateHostBuilder 函式裡面通過 UseServiceProviderFactory 方法使用 Autofac 替換 原生 的框架
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
// 使用 auto fac 代替預設的 IOC 容器
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
在 Startup 裡面新增 ConfigureContainer 方法,程式碼如下
public void ConfigureContainer(ContainerBuilder builder)
{
}
在 ConfigureContainer 裡面注入具體的需要初始化的業務模組,例如 FooModule 模組。在具體模組裡面注入實際業務的型別,如 Foo 型別,程式碼如下
public class Startup
{
// 忽略程式碼
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new FooModule());
}
}
class FooModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine($"06 FooModule");
builder.RegisterType<Foo>().As<IFoo>();
}
}
public class Foo : IFoo
{
}
public interface IFoo
{
}
現在的需求是在整合測試裡面,將 IFoo 的實際型別從 Foo 替換為 TestFoo 型別
在整合測試專案裡面,可以使用如下程式碼獲取實際的專案的依賴注入收集
var hostBuilder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
})
// 使用 auto fac 代替預設的 IOC 容器
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
var host = hostBuilder.Build();
var foo = host.Services.GetService<IFoo>();
以上的 foo 就是從收集的容器裡面獲取的 IFoo 物件,以上程式碼獲取到的是業務程式碼的 Foo 型別物件。假定需要讓容器裡面的 IFoo 的實際型別作為測試的 TestFoo 型別,就需要在實際專案的依賴注入收集完成之前,進行測試的注入
但實際上沒有在 Autofac 裡面找到任何的輔助方法可以用來實現此功能。如果是預設的應用框架,可以在 ConfigureWebHostDefaults 函式之後,通過 ConfigureServices 函式覆蓋在 Startup 的 ConfigureServices 函式注入的型別
實現方法
實現的方法是很簡單的,關於此實現為什麼能解決問題還請參閱下文的原理部分
整合測試專案不需要改動原有的業務專案即可完成測試,實現方法是在整合測試專案裡面新增 FakeAutofacServiceProviderFactory 用來替換 Autofac 的 AutofacServiceProviderFactory 型別,程式碼如下
class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
public FakeAutofacServiceProviderFactory(
ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
Action<ContainerBuilder>? configurationActionOnBefore = null,
Action<ContainerBuilder>? configurationActionOnAfter = null)
{
_configurationActionOnAfter = configurationActionOnAfter;
AutofacServiceProviderFactory =
new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
}
private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
private readonly Action<ContainerBuilder>? _configurationActionOnAfter;
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
return AutofacServiceProviderFactory.CreateBuilder(services);
}
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
_configurationActionOnAfter?.Invoke(containerBuilder);
return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
}
}
可以看到本質的 FakeAutofacServiceProviderFactory 實現就是通過 AutofacServiceProviderFactory 的屬性實現,只是在 CreateServiceProvider 方法裡面加入了委託,可以方便在單元測試裡面進行注入自己的方法
在整合測試專案裡面的使用方法如下
var hostBuilder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
})
// 使用 auto fac 代替預設的 IOC 容器
.UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
builder =>
{
builder.RegisterModule<TestModule>();
}));
傳入的委託需要注入測試的初始化模組,也就是 TestModule 需要加入注入,通過上面程式碼,可以讓 TestModule 在依賴注入的最後進行初始化。在 TestModule 裡面加入實際的測試型別注入的程式碼
class TestModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<TestFoo>().As<IFoo>();
}
}
class TestFoo : IFoo
{
}
通過上面方法就可以讓整合測試專案從容器裡面獲取 IFoo 的物件,拿到的是 TestFoo 型別,整合測試專案的程式碼如下
[TestClass]
public class FooTest
{
[ContractTestCase]
public void Test()
{
"依賴注入的時機,可以在完成收集之後,覆蓋原有的型別".Test(() =>
{
var hostBuilder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
})
// 使用 auto fac 代替預設的 IOC 容器
.UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
builder =>
{
builder.RegisterModule<TestModule>();
}));
var host = hostBuilder.Build();
var foo = host.Services.GetService<IFoo>();
Assert.IsInstanceOfType(foo, typeof(TestFoo));
});
}
}
class TestModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<TestFoo>().As<IFoo>();
}
}
class TestFoo : IFoo
{
}
class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
public FakeAutofacServiceProviderFactory(
ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
Action<ContainerBuilder>? configurationActionOnBefore = null,
Action<ContainerBuilder>? configurationActionOnAfter = null)
{
_configurationActionOnAfter = configurationActionOnAfter;
AutofacServiceProviderFactory =
new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
}
private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
private readonly Action<ContainerBuilder>? _configurationActionOnAfter;
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
return AutofacServiceProviderFactory.CreateBuilder(services);
}
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
_configurationActionOnAfter?.Invoke(containerBuilder);
return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
}
}
以上整合測試使用了 CUnit 中文單元測試框架輔助,在上面程式碼裡面,可以看到整合測試裡面的容器拿到的 IFoo 物件就是 TestFoo 型別。通過這個方法就可以在業務程式碼執行過程,注入測試需要的型別
為什麼通過以上的程式碼即可實現此功能,為什麼需要自己實現一個 FakeAutofacServiceProviderFactory 型別,為什麼不能在 AutofacServiceProviderFactory.CreateServiceProvider 方法之前注入型別,而是需要再定義一個 TestModule 模組,在測試初始化模組進行初始化。更多細節請看下文
原理
回答以上問題,需要了解各個注入方法呼叫的順序,我在程式碼裡面通過控制檯輸出各個方法的順序。標記了順序的程式碼放在本文最後
以下是按照呼叫順序的方法程式碼
Startup 的 ConfigureServices 方法
public class Startup
{
// 忽略程式碼
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
Console.WriteLine($"01 ConfigureServices");
}
}
在 Startup 的 ConfigureServices 是依賴注入中最開始呼叫的方法,這也是原生的框架自帶的方法
IHostBuilder 的 ConfigureServices 擴充套件方法
var hostBuilder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
})
.ConfigureServices(collection => Console.WriteLine($"02 ConfigureServices Delegate"))
在 IHostBuilder 的 ConfigureServices 擴充套件方法將會在 Startup 的 ConfigureServices 方法執行完成之後呼叫,因此如果只使用原生的依賴注入,可以在此方法進行覆蓋,也就是如果沒有使用 Autofac 框架,只使用原生的框架,可以在整合測試,在此方法注入測試的型別
Startup 的 ConfigureContainer 方法
public class Startup
{
// 忽略程式碼
public void ConfigureContainer(ContainerBuilder builder)
{
Console.WriteLine($"03 ConfigureContainer");
builder.RegisterModule(new FooModule());
}
}
可以看到 public void ConfigureContainer(ContainerBuilder builder)
方法的呼叫在 ConfigureServices 方法之後,在 Autofac 也通過此機制實現代替原生的依賴注入功能,也通過此方法從原生的注入獲取依賴
關於 Autofac 的實際邏輯,請參閱下文
FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法
在如上程式碼,我們編寫了 FakeAutofacServiceProviderFactory 用於替換 AutofacServiceProviderFactory 型別。在 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法將會在呼叫 ConfigureContainer 之後執行
class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
public FakeAutofacServiceProviderFactory(
ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
Action<ContainerBuilder>? configurationActionOnBefore = null,
Action<ContainerBuilder>? configurationActionOnAfter = null)
{
_configurationActionOnAfter = configurationActionOnAfter;
AutofacServiceProviderFactory =
new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
}
private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
private readonly Action<ContainerBuilder>? _configurationActionOnAfter;
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
return AutofacServiceProviderFactory.CreateBuilder(services);
}
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
Console.WriteLine($"04 FakeAutofacServiceProviderFactory");
_configurationActionOnAfter?.Invoke(containerBuilder);
return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
}
}
在以上的 CreateServiceProvider 方法將會在 Startup 的 ConfigureContainer 方法之後執行,如上面程式碼。按照上面程式碼,將會執行 _configurationActionOnAfter
委託
因此下一個執行的就是傳入的委託
IHostBuilder? hostBuilder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
})
.ConfigureServices(collection => Console.WriteLine($"02 ConfigureServices Delegate"))
// 使用 auto fac 代替預設的 IOC 容器
.UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
builder =>
{
Console.WriteLine($"05 ConfigurationActionOnAfter");
builder.RegisterModule<TestModule>();
}));
也就是如上程式碼的 ConfigurationActionOnAfter 委託
但儘管 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 在 Startup 的 ConfigureContainer 方法之後執行,實際上很多開發者不會在 Startup 的 ConfigureContainer 方法完成註冊,而是在 ConfigureContainer 裡面初始化模組。如上面程式碼,在業務邏輯註冊的模組的初始化還沒被呼叫。只有在實際的 ContainerBuilder 呼叫 Build 方法,才會執行模組的 Load 方法
因此下一個呼叫就是業務邏輯註冊的模組
FooModule 的 Load 方法
按照 Autofac 的定義,在 ConfigureContainer 的 Build 方法裡面,才會執行模組的初始化,呼叫 Load 方法
class FooModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine($"06 FooModule");
builder.RegisterType<Foo>().As<IFoo>();
}
}
在 Autofac 裡面,將會按照模組註冊的順序,呼叫模組的 Load 方法,如上面程式碼,可以看到 TestModule 在最後被註冊,因此將會最後執行
TestModule 的 Load 方法
在上面程式碼 TestModule 是最後註冊到 Autofac 的模組,也就是 TestModule 將會最後被執行
class TestModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
Console.WriteLine($"07 TestModule");
builder.RegisterType<TestFoo>().As<IFoo>();
}
}
如上面程式碼,在 TestModule 注入的測試型別,將會替換業務程式碼的實際型別
Autofac 接入的方法
通過上面的方法呼叫順序,大家也可以瞭解為什麼整合測試的程式碼這樣寫就有效果。更深入的邏輯是 Autofac 的設計,為什麼可以讓 Autofac 框架可以接入到 ASP.NET Core 應用裡面,我在此前可一直都是在 WPF 框架使用的。這個問題其實很簡單,所有的 dotnet 專案,無論是 ASP.NET Core 還是 WPF 等,都是相同的 dotnet 邏輯,裝配方式都相同,只是頂層業務邏輯實現方法有所不同,因此只需要加一點適配邏輯就能通用
從上面專案安裝的 NuGet 包可以看到,安裝了 Autofac.Extensions.DependencyInjection
庫就是提供 Autofac 與 dotnet 通用依賴注入框架連結的功能,而 ASP.NET Core 原生的框架就是基於 dotnet 通用依賴注入框架,因此就能將 Autofac 接入到 ASP.NET Core 應用
在 UseServiceProviderFactory 方法裡面,將會執行 ASP.NET Core 框架的依賴注入容器相關方法,此方法注入的 IServiceProviderFactory 帶泛形的型別,將可以支援在 Startup 方法裡面新增 ConfigureContainer 方法,引數就是 IServiceProviderFactory 的泛形
如加入了 FakeAutofacServiceProviderFactory 型別,此型別繼承了 IServiceProviderFactory<ContainerBuilder>
介面,也就是 IServiceProviderFactory 的 泛形 是 ContainerBuilder 型別,因此可以在 Startup 的 ConfigureContainer 方法引數就是 ContainerBuilder 型別
public class Startup
{
// 忽略程式碼
public void ConfigureContainer(ContainerBuilder builder)
{
}
}
而 ConfigureContainer 將會被 Microsoft.AspNetCore.Hosting.GenericWebHostBuilder 進行呼叫,在 GenericWebHostBuilder 的呼叫順序是先呼叫 ConfigureServices 再呼叫 對應的 ConfigureContainer 方法
在 Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider 方法就是實際建立容器的方法,這個方法裡面,將會先呼叫完成 ConfigureServices 的配置,然後再呼叫 ConfigureContainer 的配置,程式碼如下
public class HostBuilder : IHostBuilder
{
private void CreateServiceProvider()
{
// 忽略程式碼
var services = new ServiceCollection();
foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
{
configureServicesAction(_hostBuilderContext, services);
}
object containerBuilder = _serviceProviderFactory.CreateBuilder(services);
foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
{
containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
}
_appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
}
}
此時的 _serviceProviderFactory
將會是注入的 FakeAutofacServiceProviderFactory 型別,將會呼叫對應的 CreateBuilder 方法,也就是如下程式碼將會呼叫
class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
{
// 忽略程式碼
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
return AutofacServiceProviderFactory.CreateBuilder(services);
}
}
在 HostBuilder 的 _configureContainerActions
委託呼叫 ConfigureContainer 的邏輯,實際就是 Startup 型別裡面定義的 ConfigureContainer 方法
因此就是先呼叫 Startup 型別和 IHostBuilder 的 ConfigureServices 方法,然後再呼叫 ConfigureContainer 方法
在 Autofac 的 AutofacServiceProviderFactory 在 CreateBuilder 方法就可以拿到了原生註冊的所有型別,因為在呼叫 CreateBuilder 之前已經完成了所有的原生邏輯
在 AutofacServiceProviderFactory 的 CreateBuilder 方法將會先建立 ContainerBuilder 物件,然後呼叫 Populate 方法,從原生的 IServiceCollection 獲取註冊的型別,重新放到 ContainerBuilder 容器
public ContainerBuilder CreateBuilder(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
_configurationAction(builder);
return builder;
}
上面程式碼的 ContainerBuilder 是 Autofac 框架的,而 Populate 是擴充套件方法,和 AutofacServiceProviderFactory 都是在 Autofac.Extensions.DependencyInjection
庫提供的,通過此擴充套件方法和 AutofacServiceProviderFactory 即可實現 Autofac 和 dotnet 原生接入。在 Populate 方法從 dotnet 原生拿到註冊的型別,放入到 Autofac 的 ContainerBuilder 裡,這樣所有之前使用 dotnet 原生注入的型別就可以在 Autofac 拿到
public static void Populate(
this ContainerBuilder builder,
IEnumerable<ServiceDescriptor> descriptors,
object lifetimeScopeTagForSingletons = null)
{
if (descriptors == null)
{
throw new ArgumentNullException(nameof(descriptors));
}
builder.RegisterType<AutofacServiceProvider>().As<IServiceProvider>().ExternallyOwned();
builder.RegisterType<AutofacServiceScopeFactory>().As<IServiceScopeFactory>();
Register(builder, descriptors, lifetimeScopeTagForSingletons);
}
以上的 IEnumerable<ServiceDescriptor>
就是 IServiceCollection 型別的物件,實際程式碼是 Register 裡面拿到註冊的型別,重新放入到 Autofac 裡
private static void Register(
ContainerBuilder builder,
IEnumerable<ServiceDescriptor> descriptors,
object lifetimeScopeTagForSingletons)
{
foreach (var descriptor in descriptors)
{
if (descriptor.ImplementationType != null)
{
// Test if the an open generic type is being registered
var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
if (serviceTypeInfo.IsGenericTypeDefinition)
{
builder
.RegisterGeneric(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons);
}
else
{
builder
.RegisterType(descriptor.ImplementationType)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons);
}
}
else if (descriptor.ImplementationFactory != null)
{
var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =>
{
var serviceProvider = context.Resolve<IServiceProvider>();
return descriptor.ImplementationFactory(serviceProvider);
})
.ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
.CreateRegistration();
builder.RegisterComponent(registration);
}
else
{
builder
.RegisterInstance(descriptor.ImplementationInstance)
.As(descriptor.ServiceType)
.ConfigureLifecycle(descriptor.Lifetime, null);
}
}
}
上面程式碼拿到的 ServiceDescriptor 就是在原生框架裡面的注入型別的定義,可以看到這些都重新放到 Autofac 的容器裡面
這就是為什麼 Autofac 能拿到在 ASP.NET Core 框架裡面其他框架注入的型別的程式碼
在 HostBuilder 的 CreateServiceProvider 方法最後就是呼叫 IServiceProviderFactory 的 CreateServiceProvider 方法返回實際的容器
也就是呼叫了 Autofac 的 CreateServiceProvider 方法,程式碼如下
public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
{
if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder));
var container = containerBuilder.Build(_containerBuildOptions);
return new AutofacServiceProvider(container);
}
可以看到本質就是呼叫了 ContainerBuilder 的 Build 方法,而在 Build 方法裡面,才會初始化 Autofac 的模組。因此在 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法裡面新增的程式碼,是不會在具體業務模組的初始化模組呼叫之前被呼叫。但在 Autofac 裡面,模組的初始化順序是模組加入 Autofac 的順序,因此可以在 FakeAutofacServiceProviderFactory 裡面再加入測試的模組,測試的模組將會是最後加入的模組,也就是將會最後被執行
因此想要在接入 Autofac 框架覆蓋業務邏輯註冊的型別,就需要在 Autofac 裡面註冊一個測試使用的模組,要求這個模組最後註冊,然後在此模組裡面進行註冊型別,這樣就可以讓測試模組註冊的型別是最後註冊的,覆蓋原有的型別。而想要讓測試模組最後註冊,就需要自己實現一個繼承 IServiceProviderFactory<ContainerBuilder>
的型別,才能在 AutofacServiceProviderFactory 的 CreateServiceProvider 方法呼叫之前註冊模組
雖然我很喜歡使用 Autofac 框架,但是我覺得在接入 ASP.NET Core 時,沒有很好加入測試的機制,而讓開發者需要自己理解底層的邏輯才能進行註冊測試的型別
這裡也需要給 dotnet 的設計點贊,在一開始的 ASP.NET Core 選擇依賴注入框架時,選擇的是 dotnet 通用依賴注入框架,而 dotnet 通用依賴注入框架最底層的是使用最初的裝配器介面,在 C# 語言裡面介面的定義是最通用的,介面只約束而不定義。通過這一套傳承的定義,可以讓 10 多年前的 Autofac 框架依然可以跑在現代的應用裡面
這 10 多年也不是 Autofac 啥都不做,上面的說法只是為了說明 dotnet 的相容性特別強和最初的 dotnet 設計大佬的強大
本文的實現方法,雖然程式碼很少,但要理解 dotnet 的依賴注入和 ASP.NET Core 的依賴注入使用,和 Autofac 的接入方法。看起來就是 Autofac 的接入機制其實沒有考慮全,當然,也許是我的技術不夠,也許有更好的實現方法,還請大佬們教教我