Autofac 集成測試 在 ConfigureContainer 之後進行 Mock 注入
- 2021 年 5 月 29 日
- 筆記
- dotnet core
在使用 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 //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 的接入機制其實沒有考慮全,當然,也許是我的技術不夠,也許有更好的實現方法,還請大佬們教教我
代碼
本文所有代碼放在 github 和 gitee 歡迎小夥伴訪問