gpt4 book ai didi

c# - 无法从单例 'MyDbContext' 使用范围服务 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'

转载 作者:太空狗 更新时间:2023-10-29 19:38:22 24 4
gpt4 key购买 nike

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

编译报错:

System.InvalidOperationException: 'Cannot consume scoped service 'MyDbContext' from singleton 'Microsoft.AspNetCore.Hosting.Internal.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();
}
}

启动.cs:

services.AddHostedService<OnlineTaggerMS>();

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

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

最佳答案

你需要注入(inject)IServiceScopeFactory生成范围。否则,您无法在单例中解析作用域服务。

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

编辑:只需注入(inject) IServiceProvider 并执行以下操作就可以了:

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

第二种方式只是在内部解析 IServiceProviderScopeFactory 并且基本上做同样的事情。

关于c# - 无法从单例 'MyDbContext' 使用范围服务 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51618406/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com