我已经在我的ASP中构建了一个后台任务.本教程之后的NET Core 2.1:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1#consuming-a-scoped-service-in-a-background-task

编译给了我一个错误:

系统InvalidOperationException:"无法使用singleton"Microsoft提供的作用域服务"MyDbContext".AspNetCore.群众或部队的集合.内部的HostedServiceExecutor''

是什么导致了这个错误,以及如何修复它?

背景任务:

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

创业.反恐精英:

services.AddHostedService<OnlineTaggerMS>();

程序反恐精英:

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

完整的启动.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");
            }
        }
    }

推荐答案

我找到了出错的原因.它是CoordinatesHelper类,在后台任务OnlineTaggerMS中使用,并且是Transient-因此导致错误.我不知道为什么编译器不断抛出指向MyDbContext的错误,让我偏离了几个小时的轨道.

Asp.net相关问答推荐

调用context.Users和context.Set User有什么区别?

由于 Blazor Change Detection API,组件重新渲染被跳过.如何避免?

DBSet 不包含 Where 的定义

如何将标头中的用户名/密码传递给 SOAP WCF 服务

如何异步渲染局部视图

返回 HttpResponseMessage 时的 WebAPI Gzip

ASP.NET MVC4 jquery/javascript 包的使用

我可以在 Visual Studio 2010 中将任务列表项添加到 csHTML 吗?

如何使用文件上传控件 Select 多个文件?

带有邮箱地址参数的 WebApi 方法从 HttpClient 返回 404

如何设置asp.net身份cookie过期时间

Web Api 参数始终为空

对于每个请求,RestClient 应该是单例还是新的

是否可以在 web.config 中添加响应 http 标头?

如何使用 WebRequest 发布数据并从网页获取响应

如何在 ASP.NET MVC 中启用跨源请求

asp.net 网站上的第一页加载缓慢

如何将我的 Autofac 容器插入 ASP. NET 身份 2.1

System.Threading.Tasks - 限制并发任务的数量

ASP.NET Web 窗体中的 jQuery 验证插件