Autofac 集成测试 在 ConfigureContainer 之后进行 Mock 注入
在使用 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 的接入机制其实没有考虑全,当然,也许是我的技术不够,也许有更好的实现方法,还请大佬们教教我
代码
本文所有代码放在 github 和 gitee 欢迎小伙伴访问
Autofac 集成测试 在 ConfigureContainer 之后进行 Mock 注入的更多相关文章
- 使用 autofac 实现 asp .net core 的属性注入
使用 autofac 代替 asp .net core 默认的 IOC 容器,可实现属性注入. 之前的使用方式不受影响. 源码已开源: dotnet-campus/Autofac.Annotation ...
- 基于hk2框架的功能测试Mock注入
public Object getInstance(Class<?> clz){ return IocBean.get(clz.getName()); } public Object Mo ...
- Asp.Net MVC 之 Autofac 初步使用2 集成mvc 属性注入以及自动注入
首先看下Demo2的结构 然后下面一步步将Autofac集成到mvc中. 首先,定义Model Product.cs public class Product { public int Id ...
- Autofac容器使用属性进行WebApi自动注入
背景 使用Autofac进行依赖注入时,经常遇到的场景是在容器中进行类似如下代码的注入操作: builder.RegisterType<BackInStockSubscriptionServic ...
- IOC容器-Autofac在MVC中实现json方式注入使用
在你阅读时,默认已经了解IOC和autofac的基本用法, 我在最近的我的博客项目中运用了IOC autofac 实现了依赖注入 由于我的项目时asp.net MVC所以我目前向大家展示MVC中如何使 ...
- Autofac依赖注入框架
最近使用Autofac框架做项目的依赖注入,感觉挺好用的. 没有深入研究,只是拿来用用,具体可以去官网看看:https://autofac.org/. 这里只是贴一下最近项目的配置: public p ...
- net core WebApi——依赖注入Autofac
目录 前言 Autofac 添加一个Util来随时调用 小结 代码地址 前言 周末加班,下午犯困,整理下之前鼓捣过的东西,看过我之前的webapi系列的读者知道,我之前试过Aspect,但是升级到3. ...
- 依赖注入在 dotnet core 中实现与使用:4. 集成 Autofac
本示例使用 .net core 5 rc-1 实现. 1. 添加 Nuget 包引用 使用 Autofac 当然要添加 Autofac 的 Nuget 包,主要涉及到两个: Autofac.Exten ...
- 基于autofac的属性注入
基于autofac的属性注入 什么是属性注入 在了解属性注入之前,要先了解一下DI(Dependency Injection),即依赖注入.在ASP.NET Core里自带了一个IOC容器,而且程序支 ...
随机推荐
- 如何高效的遍历Map?你常用的不一定是最快的
微信公众号:大黄奔跑 关注我,可了解更多有趣的面试相关问题. 写在之前 如文章标题所言,遍历Map是开发过程中比较常见的行为,实现的方式也有多种方式,本文带领大家一起看看更加高效的遍历 Map. 『茴 ...
- OO第一单元总结——求导
一.基于度量分析程序结构 (一)第一次作业 (1)设计思路 本次作业只涉及到简单幂函数通过加减运算而复合而成的函数,因此笔者自然的把函数分成了函数本体以及单个的项两个部分,在笔者的设计中两个类的功能如 ...
- red and black(BFS)
Red and Black Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 40685 Accepted: 22079 D ...
- 1.8.7- HTML值label标签
1.label直接进行包裹input就可以了.
- 04- 移动APP功能测试要点以及具体业务流程测试
5.离线测试: 离线是应用程序在本地的客户端会缓存一部分数据以供程序下次调用. 1.对于一些程序,需要在登录进来后,这是没有网络的情况下可以浏览本地数据. 2.对于无网络时,刷新获取新数据时,不能获取 ...
- hdu4847 水题
题意: 你看了上面很长很长的一片英语课文之后,发现根本不用看,直接看输入输出就行了,就是给你一坨字符串,然后问你里面有几个doge(不区分大小写). 思路: 没啥说的,直接写吧 ...
- 逆向 stdio.h 函数库 fopen 函数(调试版本)
0x01 fopen 函数 函数原型:FILE *fopen(const char *filename, const char *mode) 返回值为 FILE 类型 函数功能:使用给定的模式 mod ...
- Windowsw核心编程 第13章 Windows内存结构
第1 3章 Wi n d o w s的内存结构 13.1 进程的虚拟地址空间 每个进程都被赋予它自己的虚拟地址空间.对于 3 2位进程来说,这个地址空间是4 G B,因为3 2位指针可以拥有从0 x ...
- 【vue-06】webpack npm
什么是Webpack Webpack是一款模块加载器兼打包工具,他能把各种资源,比如js,css,less转化成一个静态文件,减少页面的请求,提高效率. 安装Webpack 在安装webpack之前, ...
- lombok,Invalid byte tag in constant pool: 19
今天偶到一个奇怪的问题: 三台生产服务器部署同样的代码,同样的tomcat ,jdk等环境. 其中有一台服务器启动时报lombok-1.18.6.jar! Invalid byte tag in ...