.net 核心依赖注入是不是支持 Lazy<T>

Posted

技术标签:

【中文标题】.net 核心依赖注入是不是支持 Lazy<T>【英文标题】:Does .net core dependency injection support Lazy<T>.net 核心依赖注入是否支持 Lazy<T> 【发布时间】:2017-12-09 14:40:14 【问题描述】:

我正在尝试使用通用 Lazy 类来实例化具有 .net 核心依赖注入扩展的昂贵类。我已经注册了 IRepo 类型,但我不确定 Lazy 类的注册会是什么样子,或者它是否受支持。作为一种解决方法,我使用了这种方法http://mark-dot-net.blogspot.com/2009/08/lazy-loading-of-dependencies-in-unity.html

配置:

public void ConfigureService(IServiceCollection services)

    services.AddTransient<IRepo, Repo>();
    //register lazy

控制器:

public class ValuesController : Controller 

    private Lazy<IRepo> _repo;

    public ValuesController (Lazy<IRepo> repo)
    
        _repo = repo;
    

    [HttpGet()]
    public IActionResult Get()
    
         //Do something cheap
         if(something)
             return Ok(something);
         else
             return Ok(repo.Value.Get());
    

【问题讨论】:

“实例化一个代价高昂的类”。为什么创建该类的成本很高?创建类成本很高。他们的构造函数应该是simple,fast和reliable。 No (repository/context/...) 类应该在其构造函数中打开连接。 我无法控制的手段代价高昂。 【参考方案1】:

这是另一种方法,它支持Lazy&lt;T&gt; 的泛型注册,因此可以延迟解析任何类型。

services.AddTransient(typeof(Lazy<>), typeof(Lazier<>));

internal class Lazier<T> : Lazy<T> where T : class

    public Lazier(IServiceProvider provider)
        : base(() => provider.GetRequiredService<T>())
    
    

【讨论】:

Lazy 太重了,我建议使用更轻的版本。 @AkashKava 你指的是哪个更轻的替代品? @Akash 请展示如何“轻松”编写一个“更轻”的Lazy&lt;T&gt; 类,该类具有线程安全性,并且可以像内置类型一样完成工作。我认为可以肯定地说,Lazy&lt;T&gt; 在 99.9% 的用例中表现得足够好。 您可以自己编写,但这是一个简洁的解决方案。我的假设是 T 的昂贵初始化超过了与 Lazy 相关的任何成本。 这是一个很好的解决方案,直到有人写Laziest&lt;T&gt;【参考方案2】:

您只需为创建Lazy&lt;IRepo&gt; 对象的工厂方法添加注册即可。

public void ConfigureService(IServiceCollection services)

    services.AddTransient<IRepo, Repo>();
    services.AddTransient<Lazy<IRepo>>(provider => new Lazy<IRepo>(provider.GetService<IRepo>));

【讨论】:

谢谢,我希望会有通用的惰性注册,但似乎不可能。至少 M$ DI 不会。 你可以自己写一个通用的辅助方法。【参考方案3】:

在我看来,下面的代码应该可以完成工作(.net core 3.1)

services.AddTransient<IRepo, Repo>();
services.AddTransient(typeof(Lazy<>), typeof(Lazy<>));

【讨论】:

Avi 的好答案 这不会延迟解析服务,因为它依赖于Lazy&lt;T&gt;(T) 构造函数而不是Lazy&lt;T&gt;(Func&lt;T&gt;) 构造函数。 这是错误的。它不会被延迟加载。【参考方案4】:

Lazy 中要获取的服务将通过工厂注册方法 重新引入预期服务的new Lazy类型并使用 serviceProvider.GetRequiredService 为其实现提供。

services.AddTransient<IRepo, Repo>()
        .AddTransient(serviceProvider => new Lazy<IRepo>(() => serviceProvider.GetRequiredService<IRepo>()));

【讨论】:

【参考方案5】:

将服务注册为惰性

services.AddScoped<Lazy<AService>>();
services.AddScoped<Lazy<BService>>();

或者通过创建扩展

static class LazyServiceCollection

    public static void AddLazyScoped<T>(this IServiceCollection services)
    
        services.AddScoped<Lazy<T>>();
    


...

services.AddLazyScoped<AService>();
services.AddLazyScoped<BService>();

并使用它

[ApiController, Route("lazy")]
public class LazyController : ControllerBase

    private readonly Lazy<AService> _aService;
    private readonly Lazy<BService> _bService;

    public LazyController(Lazy<AService> aService, Lazy<BService> bService)
    
        _aService = aService;
        _bService = bService;
    

    [HttpGet("a")]
    public ActionResult GetA()
    
        _aService.Value.DoWork();
        return new OkResult();
    

    [HttpGet("b")]
    public ActionResult GetB()
    
        _bService.Value.DoWork();
        return new OkResult();
    

结果

Init AService
AService Work

【讨论】:

以上是关于.net 核心依赖注入是不是支持 Lazy<T>的主要内容,如果未能解决你的问题,请参考以下文章

.Net 核心依赖注入 IdbConnection

.NET 框架中的依赖注入容器中是不是有任何构建

如何使用 asp.net 核心中的依赖注入在工作单元模式中延迟注入存储库

如何使用构造函数依赖注入对 asp.net 核心应用程序进行单元测试

带有身份的asp.net核心中的依赖注入错误

NET Core,你必须了解无处不在的“依赖注入”