我正在将 dotnet Framework 项目更新为 dotnet Core。我有一个
UserProfileService
类来处理所有用户。它有一个 UserManager
,它是使用 dotnet 核心依赖注入来注入的。
当我尝试更新
UserProfile
(这是从 IdentityUser
派生的类)时,会抛出以下 InvalidOperationException
错误:
The instance of entity type 'UserProfile' cannot be tracked because another instance with the key value '{Id: GUID}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
使用
dbContext
中的以下代码连接Startup.cs
:
services.AddIdentityCore<UserProfile>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<AvgContext>()
.AddDefaultTokenProviders();
在此调用之前我没有从上下文中获取该用户,因此这种情况似乎不太可能发生。
有人知道会发生什么情况吗?
在此调用之前我没有从上下文中获取此用户
直接,也许不是。间接地,这仍然很重要。如果您使用 DbContext 加载用户实体,则应始终在使用
Update
或 Attach
之前验证是否跟踪实体:
var trackedUser = _dbContext.Users.Local.FirstOrDefault(x => x.UserId == userId);
如果 trackedUser 返回一个值,您应该将数据复制到其中并调用 SaveChanges,否则您可以考虑附加或使用
Update
,尽管我通常建议始终在更新场景中加载实体,因为这断言实体存在,并且您可以在应用可能过时的更新之前检查任何当前的并发标志(即 RowVersion/Timestamp)。
作为一个简单的规则,请避免使用
Update
/UpdateAsync
。使用 DbContext 时。 UserManager 提供了一些 EF 的包装器,但仍然旨在复制值。
var user = await UserManager.FindByIdAsync(userId);
// Copy values across based on what is allowed to be updated.
user.Email = dto.Email; // etc.
await UserManager.UpdateAsync(user);
如果您直接使用 DbContext 来访问 User 实体:
var user = await _dbContext.Users.SingleAsync(x => x.UserId == userId);
// copy values across based on what is allowed to be updated.
user.Email = dto.Email; // etc.
await _dbContext.SaveChangesAsync();