Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

350
Visualizações
Cannot consume scoped service 'MyDbContext' from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'

I've build a background task in my ASP.NET Core 2.1 following this tutorial: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1#consuming-a-scoped-service-in-a-background-task

Compiling gives me an error:

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

What causes that error and how to fix it?

Background task:

internal class OnlineTaggerMS : IHostedService, IDisposable
{
    private readonly CoordinatesHelper _coordinatesHelper;
    private Timer _timer;
    public IServiceProvider Services { get; }

    public OnlineTaggerMS(IServiceProvider services, CoordinatesHelper coordinatesHelper)
    {
        Services = services;
        _coordinatesHelper = coordinatesHelper;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Run every 30 sec
        _timer = new Timer(DoWork, null, TimeSpan.Zero,
            TimeSpan.FromSeconds(30));

        return Task.CompletedTask;
    }

    private async void DoWork(object state)
    {
        using (var scope = Services.CreateScope())
        {
            var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();

            Console.WriteLine("Online tagger Service is Running");

            // Run something
            await ProcessCoords(dbContext);
        }          
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    private async Task ProcessCoords(MyDbContext dbContext)
    {
        var topCoords = await _coordinatesHelper.GetTopCoordinates();

        foreach (var coord in topCoords)
        {
            var user = await dbContext.Users.SingleOrDefaultAsync(c => c.Id == coord.UserId);

            if (user != null)
            {
                var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                //expire time = 120 sec
                var coordTimeStamp = DateTimeOffset.FromUnixTimeMilliseconds(coord.TimeStamp).AddSeconds(120).ToUnixTimeMilliseconds();

                if (coordTimeStamp < now && user.IsOnline == true)
                {
                    user.IsOnline = false;
                    await dbContext.SaveChangesAsync();
                }
                else if (coordTimeStamp > now && user.IsOnline == false)
                {
                    user.IsOnline = true;
                    await dbContext.SaveChangesAsync();
                }
            }
        }
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}

Startup.cs:

services.AddHostedService<OnlineTaggerMS>();

Program.cs:

 public class Program
{
    public static void Main(string[] args)
    {
        var host = BuildWebHost(args);

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            try
            {
                var context = services.GetRequiredService<TutorDbContext>();
                DbInitializer.Initialize(context);
            }
            catch(Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred while seeding the database.");
            }
        }

        host.Run();
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}

Full startup.cs:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader()
                .AllowCredentials());
        });

        services.AddDbContext<MyDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        // ===== Add Identity ========
        services.AddIdentity<User, IdentityRole>()
            .AddEntityFrameworkStores<TutorDbContext>()
            .AddDefaultTokenProviders();

        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); // => remove default claims
        services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(cfg =>
            {
                cfg.RequireHttpsMetadata = false;
                cfg.SaveToken = true;
                cfg.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidIssuer = Configuration["JwtIssuer"],
                    ValidAudience = Configuration["JwtIssuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
                    ClockSkew = TimeSpan.Zero // remove delay of token when expire
                };
            });

        //return 401 instead of redirect
        services.ConfigureApplicationCookie(options =>
        {
            options.Events.OnRedirectToLogin = context =>
            {
                context.Response.StatusCode = 401;
                return Task.CompletedTask;
            };

            options.Events.OnRedirectToAccessDenied = context =>
            {
                context.Response.StatusCode = 401;
                return Task.CompletedTask;
            };
        });

        services.AddMvc();

        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new Info { Version = "v1", Title = "xyz", });

            // Swagger 2.+ support
            var security = new Dictionary<string, IEnumerable<string>>
            {
                {"Bearer", new string[] { }},
            };

            c.AddSecurityDefinition("Bearer", new ApiKeyScheme
            {
                Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
                Name = "Authorization",
                In = "header",
                Type = "apiKey"
            });
            c.AddSecurityRequirement(security);
        });

        services.AddHostedService<OnlineTaggerMS>();
        services.AddTransient<UsersHelper, UsersHelper>();
        services.AddTransient<CoordinatesHelper, CoordinatesHelper>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IServiceProvider serviceProvider, IApplicationBuilder app, IHostingEnvironment env, TutorDbContext dbContext)
    {
        dbContext.Database.Migrate();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCors("CorsPolicy");
        app.UseAuthentication();
        app.UseMvc();

        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("v1/swagger.json", "xyz V1");
        });

        CreateRoles(serviceProvider).GetAwaiter().GetResult();
    }

    private async Task CreateRoles(IServiceProvider serviceProvider)
    {
        var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
        var UserManager = serviceProvider.GetRequiredService<UserManager<User>>();
        string[] roleNames = { "x", "y", "z", "a" };
        IdentityResult roleResult;

        foreach (var roleName in roleNames)
        {
            var roleExist = await RoleManager.RoleExistsAsync(roleName);
            if (!roleExist)
            {
                roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
            }
        }
        var _user = await UserManager.FindByEmailAsync("xxx");

        if (_user == null)
        {
            var poweruser = new User
            {
                UserName = "xxx",
                Email = "xxx",
                FirstName = "xxx",
                LastName = "xxx"
            };
            string adminPassword = "xxx";

            var createPowerUser = await UserManager.CreateAsync(poweruser, adminPassword);
            if (createPowerUser.Succeeded)
            {
                await UserManager.AddToRoleAsync(poweruser, "xxx");
            }
        }
    }
over 4 years ago · Santiago Trujillo
5 Respostas
Responde à pergunta

0

You still need to register MyDbContext with the service provider. Usually this is done like so:

services.AddDbContext<MyDbContext>(options => {
    // Your options here, usually:
    options.UseSqlServer("YourConnectionStringHere");
});

If you also posted your Program.cs and Startup.cs files, it may shed some more light on things, as I was able to quickly setup a test project implementing the code and was unable to reproduce your issue.

over 4 years ago · Santiago Trujillo Relatório

0

You need to inject IServiceScopeFactory to generate a scope. Otherwise you are not able to resolve scoped services in a singleton.

using (var scope = serviceScopeFactory.CreateScope())
{
  var context = scope.ServiceProvider.GetService<MyDbContext>();
}

Edit: It's perfectly fine to just inject IServiceProvider and do the following:

using (var scope = serviceProvider.CreateScope()) // this will use `IServiceScopeFactory` internally
{
  var context = scope.ServiceProvider.GetService<MyDbContext>();
}

The second way internally just resolves IServiceProviderScopeFactory and basically does the very same thing.

over 4 years ago · Santiago Trujillo Relatório

0

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.

over 4 years ago · Santiago Trujillo Relatório

0

Though @peace answer worked for him, if you do have a DBContext in your IHostedService you need to use a IServiceScopeFactory.

To see an amazing example of how to do this check out this answer How should I inject a DbContext instance into an IHostedService?.

If you would like to read more about it from a blog, check this out .

over 4 years ago · Santiago Trujillo Relatório

0

For Entity Framework DbContext classes there is a better way now with:

services.AddDbContextFactory<MyDbContext>(options => options.UseSqlServer(...))

Then you can just inject the factory in your singleton class like this:

 IDbContextFactory<MyDbContext> myDbContextFactory

And finally use it like this:

using var myDbContex = _myDbContextFactory.CreateDbContext();
over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda