Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Gordon Saxby 1461 posts 1883 karma points
    Jun 09, 2022 @ 10:19
    Gordon Saxby
    0

    Trouble with Dependency Injection

    I am new to Umbraco 9 and Dependency Injection, so would appreciate some tips on how to resolve this runtime error I'm getting:

    InvalidOperationException: Error while validating the service descriptor 'ServiceType: MyKRingWebsite.Hangfire.Components.HangfireJobs Lifetime: Singleton ImplementationType: MyKRingWebsite.Hangfire.Components.HangfireJobs': Cannot consume scoped service 'Umbraco.Cms.Core.Security.IMemberManager' from singleton 'MyKRingWebsite.Zoho.Interfaces.IZohoApiService'. Microsoft.Extensions.DependencyInjection.ServiceProvider.ValidateService(ServiceDescriptor descriptor) Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(ICollection

    "ZohoApiService" is a Singleton because it uses RestSharp and they say

    Do not instantiate RestClient for each HTTP call. RestSharp creates a new instance of HttpClient internally, and you will get lots of hanging connections, and eventually exhaust the connection pool. If you use a dependency - injection container, register your API client as a singleton.

    I have a number of projects making up the solution. "Hangfire" and "Zoho" do not reference each other directly. However, "Hangfire" references "Member" and "Member" references "Zoho".

    I think pretty much everything is is added as "Transient" at the moment, except for one other that also uses RestSharp.

    Any help, pointers, etc would be most welcome!

  • Dennis 75 posts 398 karma points MVP 2x
    Jun 10, 2022 @ 19:23
    Dennis
    3

    Hi Gordon,

    I recommend you check out the IServiceScopeFactory interface. Obviously you can't consume a scoped service in a singleton, but you can create a new scope inside a method in your singleton.

    As a short example:

    public class MySingleton
    {
        private readonly IServiceScopeFactory _scopeFactory;
    
        public MySingleton(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }
    
        public void MyMethod()
        {
            using var scope = _scopeFactory.CreateScope();
            var myService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
    
            myService.DoSomething();
        }
    }
    

    So basically: to consume a scoped service inside a singleton, you have to create a scope inside the method where you want to use the scoped service.

    Read some more about the service scope factory in the microsoft documentation.

  • Gordon Saxby 1461 posts 1883 karma points
    Jun 14, 2022 @ 09:47
    Gordon Saxby
    0

    Thanks for the reply and I will look at IServiceScopeFactory.

    Unfortunately for me, "Obviously you can't consume a scoped service in a singleton" isn't obvious but is something I need to find out about / understand!!

  • Dennis 75 posts 398 karma points MVP 2x
    Jun 15, 2022 @ 06:25
    Dennis
    105

    oh, forgive me for making that assumption. Here's what I know about the subject:

    There are three different ways to register an object / service in the DI container:

    1. Singleton: a singleton service has a single instance in the lifetime of the entire DI container
    2. Scoped: a scoped service has a single instance in the lifetime of a "scope" (more on that later)
    3. Transient: a transient service has a unique instance per dependency

    This has to do with lifetime management. Objects and services need to be created and destroyed at different times in different ways.

    Singleton services can be injected everywhere, because you know they will exist until the whole DI container is disposed. Scoped and transient services are meant to be created and disposed quickly. That is why scopes are introduced.

    To consume scoped and transient services, you need to create a scope and dispose of it when you're done. That way, you properly manage the lifetime of these services. You're not supposed to use the services outside the lifetime of the scope, because they or their dependencies may have already been disposed. This is the reason why you can't inject a scoped service inside a singleton, because there's nothing to manage the lifetime of the services that you consume and you'll likely use them outside their scope.

    The IServiceScopeFactory is a service that creates a scope for you. This scope is disposable and should be used with a using statement. Inside the using statement, you can use scoped services as much as you want.

    Now you may wonder: "but what about controllers and middlewares? I don't create a scope for them!" A scope is created by the asp.net core framework at the beginning of a request and your controller is in fact a scoped service! The framework takes care of services in the lifetime of a request, so you don't have to worry about them.

    Now is the question: which lifetime should I choose for my services? The way I make this decision is by asking myself: "Does this service require context from a request?" If no, register as a singleton. If yes, register it as scoped. I haven't ever had a need to register a transient service, though I'm sure there are some good usecases for them.

    If at some point, you do really need to consume a scoped service inside a singleton, then the IServiceScopeFactory is your friend.

    I hope that helps :D

  • Gordon Saxby 1461 posts 1883 karma points
    Jun 15, 2022 @ 08:33
    Gordon Saxby
    0

    Thanks for that detailed explanation.

    I can't remember where I saw it but I read that the default / standard was Transient, but I can't remember if there was a description of why.

    Also, I wonder if I need to review the use of RestSharp if it is going to "force" me to register services that use it as Singleton. I found out a bit about IHttpClientFactory yesterday that might make for a better solution?

    Luckily, I am able to spend the time to (try and) get it right on this project - at the moment, anyway :-)

Please Sign in or register to post replies

Write your reply to:

Draft