Does .net core dependency injection support Lazy<T>

C#Dependency Injectionasp.net Core.Net CoreLazy Initialization

C# Problem Overview


I am trying to use the generic Lazy class to instantiate a costly class with .net core dependency injection extension. I have registered the IRepo type, but I'm not sure what the registration of the Lazy class would look like or if it is even supported. As a workaround I have used this method http://mark-dot-net.blogspot.com/2009/08/lazy-loading-of-dependencies-in-unity.html

config:

public void ConfigureService(IServiceCollection services)
{
    services.AddTransient<IRepo, Repo>();
    //register lazy
}

controller:

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());
    }
}

C# Solutions


Solution 1 - C#

Here's another approach which supports generic registration of Lazy<T> so that any type can be resolved lazily.

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

internal class Lazier<T> : Lazy<T> where T : class
{
    public Lazier(IServiceProvider provider)
        : base(() => provider.GetRequiredService<T>())
    {
    }
}

Solution 2 - C#

You only need to add a registration for a factory method that creates the Lazy<IRepo> object.

public void ConfigureService(IServiceCollection services)
{
    services.AddTransient<IRepo, Repo>();
    services.AddTransient<Lazy<IRepo>>(provider => new Lazy<IRepo>(provider.GetService<IRepo>));
}

Solution 3 - C#

To my opinion, the code below should do the work(.net core 3.1)

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

Solution 4 - C#

Services that are to be fetched in Lazy will be re-introduced by the factory registration method with the new Lazy of the intended service type and provided for its implementation using serviceProvider.GetRequiredService.

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

Solution 5 - C#

To register services as lazy

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

Or by creating an extension

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

...

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

And use it

[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();
    }
}

Result

Init AService
AService Work

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionATerryView Question on Stackoverflow
Solution 1 - C#Michael PetitoView Answer on Stackoverflow
Solution 2 - C#Scott ChamberlainView Answer on Stackoverflow
Solution 3 - C#avi siboniView Answer on Stackoverflow
Solution 4 - C#Reza JenabiView Answer on Stackoverflow
Solution 5 - C#Edwin SulaimanView Answer on Stackoverflow