gpt4 book ai didi

asp.net-core - 由于异常 'A second operation started on this context before a previous',无法以静态方法在 ASP.NET Core 中播种数据

转载 作者:行者123 更新时间:2023-12-04 09:27:09 27 4
gpt4 key购买 nike

我正在尝试使用以下代码为我的数据库做种:

Startup.Configure :

app.UseCors("AllowAll")
.UseMiddleware<JwtBearerMiddleware>()
.UseAuthentication()
.SeedDatabase() <= here
.UseHttpsRedirection()
.UseDefaultFiles()
.UseMvc()
.UseSpa(SpaApplicationBuilderExtensions => { });

SeedDatabase方法:

public static IApplicationBuilder SeedDatabase(this IApplicationBuilder app)
{
IServiceProvider serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider;
try
{
UserManager<ApplicationUser> userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
RoleManager<IdentityRole> roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
IConfiguration configuration = serviceProvider.GetService<IConfiguration>();
ThePLeagueContext dbContext = serviceProvider.GetService<ThePLeagueContext>();
DataBaseInitializer.SeedUsers(userManager, roleManager, configuration, dbContext);
DataBaseInitializer.SeedTeams(dbContext);

}
catch (Exception ex)
{
ILogger<Program> logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}

return app;
}

一切正常,直到我添加 ThePLeagueContext dbContext = serviceProvider.GetService<ThePLeagueContext>();然后是 DataBaseInitializer.SeedTeams(dbContext)

DataBaseInitializer.SeedTeams(dbContext) :

public static async void SeedTeams(ThePLeagueContext dbContext)
{
List<Team> teams = new List<Team>();

// 7 because we have 7 leagues
for (int i = 0; i < 7; i++)...

if (dbContext.Teams.Count() < teams.Count)
{
foreach (Team newTeam in teams)
{
await dbContext.Teams.AddAsync(newTeam);
await dbContext.SaveChangesAsync();
}
}
}

当我尝试使用上述代码对数据库进行播种时,出现以下异常:

System.InvalidOperationException: 'A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. This could also be caused by a nested query being evaluated on the client, if this is the case rewrite the query avoiding nested invocations.'

我的数据库上下文注册了生命周期Scoped .

我发现了两个解决方法:

  1. 当我将数据库上下文更改为 Transient 时播种问题消失了。然而,这会导致应用程序出现其他问题,因此我无法使用 Transient
  2. 当我调用 DatabaseInitializer.SeedTeams(dbContext)DatabaseInitializer.SeedUsers(...) 里面方法,这也有效,我不知道为什么。

DatabaseInitializer.SeedUsers(...)方法:

public async static void SeedUsers(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IConfiguration configuration, ThePLeagueContext dbContext)
{
string[] roles = new string[] { AdminRole, SuperUserRole, UserRole };

foreach (string role in roles)
{
if (!roleManager.Roles.Any(r => r.Name == role))
{
IdentityRole newRole = new IdentityRole
{
Name = role,
NormalizedName = role.ToUpper()
};
await roleManager.CreateAsync(newRole);

if (role == AdminRole)
{
await roleManager.AddClaimAsync(newRole, new Claim(Permission, ModifyPermission));
}
else if (role == SuperUserRole)
{
await roleManager.AddClaimAsync(newRole, new Claim(Permission, RetrievePermission));
}
else
{
await roleManager.AddClaimAsync(newRole, new Claim(Permission, ViewPermission));
}
}
}

ApplicationUser admin = new ApplicationUser()...

ApplicationUser sysAdmin = new ApplicationUser()...;

PasswordHasher<ApplicationUser> password = new PasswordHasher<ApplicationUser>();

if (!userManager.Users.Any(u => u.UserName == admin.UserName))
{
string hashed = password.HashPassword(admin, configuration["ThePLeagueAdminInitPassword"]);
admin.PasswordHash = hashed;

await userManager.CreateAsync(admin);
await userManager.AddToRoleAsync(admin, AdminRole);
}

if (!userManager.Users.Any(u => u.UserName == sysAdmin.UserName))
{
string hashed = password.HashPassword(sysAdmin, configuration["ThePLeagueAdminInitPassword"]);
sysAdmin.PasswordHash = hashed;

await userManager.CreateAsync(sysAdmin);
await userManager.AddToRoleAsync(sysAdmin, AdminRole);
}

SeedTeams(dbContext);

}

有什么方法可以使用两个单独的静态异步方法来为数据库播种并使我的上下文保持作用域?

最佳答案

所以我喜欢让事情井井有条并分开。因此我会做类似的事情:

public static class SeedData 
{
public static void Populate(IServiceProvider services)
{
ApplicationDbContext context = services.GetRequiredService<ApplicationDbContext>();
if (!context.SomeDbSet.Any())
{
// ...code omitted for brevity...
);
context.SaveChanges();
}
}
public static class IdentitySeedData 
{
public static async Task Populate(IServiceProvider services)
{
UserManager<ApplicationUser> userManager = services.GetService<UserManager<ApplicationUser>>();
RoleManager<IdentityRole> roleManager = services.GetService<RoleManager<IdentityRole>>();
IConfiguration configuration = services.GetService<IConfiguration>();
ApplicationDbContext context = services.GetRequiredService<ApplicationDbContext>();

if (!context.Users.Any())
{
// ...code omitted for brevity...
await userManager.CreateAsync(sysAdmin);
await userManager.AddToRoleAsync(sysAdmin, AdminRole);
);
context.SaveChanges();
}
}

然后是最重要的:

public static class DatabaseInitializer 
{
public static void Initialize(IServiceProvider services)
{
IdentitySeedData.Populate(services).Wait();
SeedData.Populate(services);
}
}

免责声明:我没有运行代码。因此,如果需要进行一些调整,请告诉我。我会做出调整。测试这个有点耗时。

关于asp.net-core - 由于异常 'A second operation started on this context before a previous',无法以静态方法在 ASP.NET Core 中播种数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59539480/

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