Cannot Consume Scoped Service Imongodbcontext from Singleton Iactiveusersservice After Upgrade to ASP.NET Core 2.0

Cannot consume scoped service IMongoDbContext from singleton IActiveUsersService after upgrade to ASP.NET Core 2.0

You can't use a service with a smaller lifetime. Scoped services only exist per-request, while singleton services are created once and the instance is shared.

Now only one instance of IActiveUsersService exists in the app. But it wants to depend on MongoDbContext, which is Scoped, and is created per-request.

You will have to either:

  1. Make MongoDbContext a Singleton, or
  2. Make IActiveUsersService Scoped, or
  3. Pass MongoDbContext into the user service as a function argument

Cannot consume scoped service from singleton

You're almost there, but you've left DBContext as a dependency in TokenService's constructor. Remove that and you'll no longer receive the error.


public TokenService(
IConfiguration configuration,
IMemoryCache memoryCache,
IHttpClientFactory clientFactory,
DBContext DBcontext,
IServiceScopeFactory scopeFactory)

However, you're not quite following the recommendation for dealing with DbContext's in a singleton service. Instead of creating a single instance of DBContext in the constructor and storing it as a field, create a scope and a corresponding DBContext whenever you need it. In your case, that's in the getOrg method.

Follow these steps to achieve that:

  1. Remove the _DBcontext field from your TokenService class:

    public DBContext _DBcontext;

  2. Remove the associated assignment from the TokenService constructor:

    _DBcontext = _scopeFactory.CreateScope().ServiceProvider.GetRequiredService<DBcontext>();

  3. In getOrg, create a scope, resolve an instance of DBContext and, lastly, dispose of the scope:

    public async Task getOrg()
    {
    var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
    var response = await _client_NP.SendAsync(request);
    var json = await response.Content.ReadAsStringAsync();
    OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);

    using (var scope = _scopeFactory.CreateScope())
    {
    var dbContext = scope.ServiceProvider.GetRequiredService<DBcontext>();

    foreach (var item in model.resources)
    {
    var g = Guid.Parse(item.guid);
    var x = dbContext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
    if (x == null)
    {
    dbContext.Organizations.Add(new Organizations
    {
    OrgGuid = g,
    Name = item.name,
    CreatedAt = item.created_at,
    UpdatedAt = item.updated_at,
    Timestamp = DateTime.Now,
    Foundation = 3
    });
    }
    else if (x.UpdatedAt != item.updated_at)
    {
    x.CreatedAt = item.created_at;
    x.UpdatedAt = item.updated_at;
    x.Timestamp = DateTime.Now;
    }
    }

    await dbContext.SaveChangesAsync();
    }
    }

Instead of using the _DBContext field, the code above creates a local, properly-scoped variable dbContext and uses that instead.

C# Dependency Injection Cannot Consume Scoped?

IHostedServices are registered as singletons. You are injecting ICarbonService into ValidUntilTimerService, which means you are injecting a scoped service into a singleton service.

If we consider the lifetimes of these two types of services:

  • Scoped services are created once per request. This means, when a scoped service is instantiated, it stays alive until the end of the request.

  • When singleton service gets instantiated, it stays alive until the app shuts down.

we realize that it doesn't make much sense to consume scoped service from a singleton service. This is what would happen in that situation:

When singleton service (ValidUntilTimerService in your case) gets instantiated, its scoped dependency (ICarbonService in your case) also gets instantiated and injected to the singleton service. When the request is completed, the singleton service stays alive, but the scoped service gets disposed. Now your singleton service ended up with a "dead" dependency (_carbonService field in your case).

The above scenario is not possible. It is just a "what if" scenario. The point is, you cannot consume a service that is created per request from a service that can be created without request (e.g. upon app startup).

Now, that explains the cause of your issue, but let's see what you can do to solve it.

POSSIBLE SOLUTION

You can use IServiceScopeFactory to create your own scope inside of the ValidUntilTimerService:

public class ValidUntilTimerService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
private IServiceScopeFactory _serviceScopeFactory;

public ValidUntilTimerService(ILogger<ValidUntilTimerService> logger, IServiceScopeFactory serviceScopeFactory)
{
_logger = logger;
_serviceScopeFactory = serviceScopeFactory;
}

// ...
using (var scope = _serviceScopeFactory.CreateScope())
{
ICarbonService carbonService = scope.ServiceProvider.GetService(typeof(ICarbonService));
// ...
}
// ...
}

Cannot consume scoped service 'MyDbContext' from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'

I found the reason of an error. It was the CoordinatesHelper class, which is used in the the background task OnlineTaggerMS and is a Transient - so it resulted with an error. I have no idea why compiler kept throwing errors pointing at MyDbContext, keeping me off track for few hours.

Cannot consume scoped service xxx from singleton yyy

For your question, yes it is the right way to do the things.

I wanted to summarize the answer in a better way, but then I did some research on this topic and I found an article that deals with questions like you asked.

I would suggest having a look at the article since it has "Best Practices" sections on how to approach certain problems.

Cannot consume scoped service MyDbContext from singleton - InvalidOperationException

Well, this is pretty common to trip over in asp.net core, and there is a full article about it in dotnetcoretutorials:

...because it’s actually the Service DI of ASP.net
Core trying to make sure you don’t trip yourself up. Although it’s not
foolproof (They still give you enough rope to hang yourself), it’s
actually trying to stop you making a classic DI scope mistake.

the conclusion at the end of it all is simple: because the ChildService is scoped, but the FatherService is singleton, it’s not going to allow us to run:

...is that transient is “everytime this service is requested, create a
new instance”, so technically this is correct behaviour (Even though
it’s likely to cause issues). Whereas a “scoped” instance in ASP.net
Core is “a new instance per page request” which cannot be fulfilled
when the parent is singleton.

.net core 3 Problem while registering ApplicationDbContext for dependency injection

You are trying to inject a scoped service (ApplicationDbContext) in a singleton, that is not allowed since it will basically be captured as a singleton. If you want to use the DbContext, you will need to register the class that are going to use it as Scoped or Transient instead of Singleton

Cannot consume scoped service 'IHttpContextAccessor' from singleton 'IMyCustomThing'

As we sussed out in the comments, you have something registering IHttpContextAccessor as a scoped service. Remove that.

services.AddHttpContextAccessor() registers it as a singleton as it's designed to be used.



Related Topics



Leave a reply



Submit