ASP.NET Core 6 MVC数据填充器未生成Category,AppUserId非空约束报错
问题:Category数据填充失败,AppUserId为空约束报错
使用PostgreSQL数据库,AppUser与Contact、Category均为一对多关系。手动创建Category时字段可正常填充,但数据填充器运行时,AppUser和Contact能正常创建,Category却无法生成,终端报错:
null value in column "AppUserId" of relation "Categories" violates not-null constraint
Contact的AppUserId可正确赋值,Category却不行,代码如下:
using ContactPro.Data; using ContactPro.Models; using Microsoft.AspNetCore.Identity; namespace ContactPro.Helpers { public class DataSeeder { private readonly ApplicationDbContext _dbContext; private readonly UserManager<AppUser> _userManager; private static string appUserId; public DataSeeder(ApplicationDbContext dbContext, UserManager<AppUser> userManager) { _dbContext = dbContext; _userManager = userManager; } public async Task SeedDataAsync() { await SeedUsersAsync(); await SeedContactsAsync(); await SeedCategoriesAsync(); } private async Task SeedUsersAsync() { if (_dbContext.Users.Any()) { return; } var demoUser = new AppUser() { UserName = "demouser@mail.com", Email = "demouser@mail.com", FirstName = "Demo", LastName = "User", EmailConfirmed = true, }; await _userManager.CreateAsync(demoUser, "xxxxxx"); await _dbContext.SaveChangesAsync(); appUserId = _userManager.Users.FirstOrDefault(u => u.Email == "demouser@mail.com").Id; } private async Task SeedContactsAsync() { if (!_dbContext.Contacts.Any()) { var contact = new List<Contact>() { new Contact() { FirstName = "John", LastName = "Dickens", Address1 = "12 Main St.", City = "Concord", States = ContactPro.Enums.States.NH, ZipCode = 03101, Email = "john@mail.com", AppUserId = appUserId, } }; await _dbContext.Contacts.AddRangeAsync(contact); await _dbContext.SaveChanges(); } } private async Task SeedCategoriesAsync() { if (!_dbContext.Categories.Any()) { var category = new List<Category>() { new Category() { Name = "_UnCategorized", AppUserId = appUserId, }, }; await _dbContext.Categories.AddRangeAsync(category); await _dbContext.SaveChanges(); } } } }
原因分析
核心问题出在静态字段appUserId的赋值逻辑漏洞:
- 当数据库中已经存在用户数据时,
SeedUsersAsync会直接return,跳过appUserId = ...的赋值步骤,导致appUserId保持null。 - Contact能正常创建,大概率是因为之前运行填充器时
appUserId已经被赋值,静态字段在应用生命周期内会保留值;但应用重启后,或者首次运行时用户已存在,就会触发Category的空值报错。 - 另外,
_userManager.CreateAsync执行后,demoUser.Id已经自动生成,没必要再通过_userManager.Users.FirstOrDefault查询,这一步不仅多余,还可能因上下文未及时刷新导致查询异常。
修复方案
方案1:移除静态字段,直接传递用户ID(推荐)
把静态appUserId去掉,通过方法参数传递用户ID,避免静态变量的生命周期问题:
// 修改SeedUsersAsync,返回用户ID private async Task<string> SeedUsersAsync() { var existingUser = await _userManager.FindByEmailAsync("demouser@mail.com"); if (existingUser != null) { return existingUser.Id; } var demoUser = new AppUser() { UserName = "demouser@mail.com", Email = "demouser@mail.com", FirstName = "Demo", LastName = "User", EmailConfirmed = true, }; await _userManager.CreateAsync(demoUser, "xxxxxx"); // 无需额外调用SaveChanges,CreateAsync内部已处理 return demoUser.Id; } // 修改SeedDataAsync,传递用户ID给后续方法 public async Task SeedDataAsync() { var appUserId = await SeedUsersAsync(); await SeedContactsAsync(appUserId); await SeedCategoriesAsync(appUserId); } // 修改SeedContactsAsync和SeedCategoriesAsync,接收用户ID参数 private async Task SeedContactsAsync(string appUserId) { if (!_dbContext.Contacts.Any()) { var contact = new List<Contact>() { new Contact() { FirstName = "John", LastName = "Dickens", Address1 = "12 Main St.", City = "Concord", States = ContactPro.Enums.States.NH, ZipCode = 03101, Email = "john@mail.com", AppUserId = appUserId, } }; await _dbContext.Contacts.AddRangeAsync(contact); await _dbContext.SaveChangesAsync(); } } private async Task SeedCategoriesAsync(string appUserId) { if (!_dbContext.Categories.Any()) { var category = new List<Category>() { new Category() { Name = "_UnCategorized", AppUserId = appUserId, }, }; await _dbContext.Categories.AddRangeAsync(category); await _dbContext.SaveChangesAsync(); } }
方案2:保留静态字段,但补全赋值逻辑
如果一定要用静态字段,修改SeedUsersAsync,确保无论用户是否存在都能赋值:
private async Task SeedUsersAsync() { var existingUser = await _userManager.FindByEmailAsync("demouser@mail.com"); if (existingUser != null) { appUserId = existingUser.Id; return; } var demoUser = new AppUser() { UserName = "demouser@mail.com", Email = "demouser@mail.com", FirstName = "Demo", LastName = "User", EmailConfirmed = true, }; await _userManager.CreateAsync(demoUser, "xxxxxx"); appUserId = demoUser.Id; }
额外优化点
- 移除
SeedUsersAsync中的await _dbContext.SaveChangesAsync();,因为_userManager.CreateAsync内部已经会调用SaveChanges,重复调用属于冗余操作。 - 使用
_userManager.FindByEmailAsync代替_userManager.Users.FirstOrDefault,这是Identity框架提供的专门查询方法,更高效且符合规范。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

