在某些情况, 我们希望能延迟一个依赖的初始化. 如果使用的是 autofac, 我们可以通过注入 Lazy
我们对 autofac GitHub 上提供的一个例子进行进行简单改造, 跑起来看看. 原 Example 的链接 https://github.com/autofac/Examples/tree/master/src/AspNetCoreExample
微改后的代码
- [Route("api/[controller]")]
- public class ValuesController : Controller
- {
- private readonly Lazy<IValuesService> _valuesService;
- public ValuesController(Lazy<IValuesService> valuesService)
- {
- _valuesService = valuesService;
- }
- // GET api/values
- [HttpGet]
- public IEnumerable<string> Get()
- {
- // Kestrel 模式下这里会输出 false, 实例尚未创建
- Console.WriteLine(_valuesService.IsValueCreated);
- // 调用 Lazy<T > 的 Value 属性才真正创建实例
- return this._valuesService.Value.FindAll();
- }
- }
直到目前 core2.1 版本, 自带的 DI 依旧未支持延迟加载, 如果我们尝试在使用自带 DI 的情况下套用上述代码, 会得到一个异常, 例如:
An unhandled exception occurred while processing the request.
- InvalidOperationException: Unable to resolve service for type 'System.Lazy`1[webApplication9.Services.IValuesService]' while attempting to activate 'WebApplication9.Controllers.ValuesController'.
- Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
如何利用 core 自带的 DI 实现呢? 如果我们尝试百度, 可能会搜到类似下面的答案.
services.AddTransient(typeof(Lazy<>));
那么这样的做法是否能解决我们的问题呢, 为了简化演示代码. 我们创建一个控制台程序并引用 Microsoft.Extensions.DependencyInjection.
- class Program
- {
- static void Main(string[] args)
- {
- var services = new ServiceCollection();
- services.AddScoped<ITestService, TestService>();
- services.AddTransient(typeof(Lazy<>));
- var serviceProvider = services.BuildServiceProvider();
- using (var scope = serviceProvider.CreateScope() )
- {
- var service = scope.ServiceProvider.GetService<Lazy<ITestService>>();
- // 这边令人遗憾地输出了 true, 也就是说, 这种方式的延迟注入是失败的
- Console.WriteLine(service.IsValueCreated);
- }
- }
- }
在查阅 Stack Overflow 的时候, 我看到了这样的解决方案, 感觉还是挺简单实用的, 分享给大家.
原贴地址: https://stackoverflow.com/questions/44934511/does-net-core-dependency-injection-support-lazyt
- public class LazyLoader<T> : Lazy<T>
- {
- public LazyLoader(IServiceProvider sp) : base(sp.GetRequiredService<T>)
- {
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- var services = new ServiceCollection();
- services.AddScoped<ITestService, TestService>();
- // services.AddScoped(typeof(Lazy<>), typeof(LazyLoader<>)); 也可以, 区别不大
- services.AddTransient(typeof(Lazy<>), typeof(LazyLoader<>));
- var serviceProvider = services.BuildServiceProvider();
- using (var scope = serviceProvider.CreateScope())
- {
- var service = scope.ServiceProvider.GetService<Lazy<ITestService>>();
- Console.WriteLine(service.IsValueCreated); // 输出 false
- // 下面输出 true, 延迟注入的对象和正常注入的对象, 本质上不会有差别
- Console.WriteLine(service.Value == scope.ServiceProvider.GetService<ITestService>());
- }
- }
- }
实现原理比较简单, 在 LazyLoader 中注入 ServiceProvider, 调用父类的 Value 属性时会执行委托, 从 ServiceProvider 中获取到对应得依赖实例.
来源: https://www.cnblogs.com/blurhkh/p/9545311.html