我正在尝试在
ResetPasswordAsync
上使用 Microsoft.AspNetCore.Identity.UserManager
方法重置用户的密码。
// Inject the UserManager
[Inject] public UserManager<ApplicationManager> UserManager { get; set; }
// Inject the factory
[Inject] public IDbContextFactory<DataContext> Factory { get; set; }
...
// Reset the password
async Task ResetPasswordAsync (ApplicationUser user)
{
...
var context = Factory.CreateDbContext();
var password = CreatePassword();
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
// Tried setting the state to detached, but it didn't work
dataContext.Entry(user).State = EntityState.Detached;
// This line throws the exception
await userManager.ResetPasswordAsync(user, token, password );
}
但是,最后一行抛出了这个异常。
无法跟踪实体类型“ApplicationUser”的实例,因为已跟踪具有该键值的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
我使用实体框架核心作为数据上下文,使用 blazor 作为用户界面。
作为一般规则,避免传递分离的实体,它会导致许多这样的问题。
当你有这样的代码时:
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
您正在传递传递到方法中的分离的“用户”引用。相反,简化。如果您的方法只有用户 ID,您会做什么?
async Task ResetPasswordAsync (int userId)
{
var user = await UserManager.FindByIdAsync(userId);
var password = CreatePassword();
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
await userManager.ResetPasswordAsync(user, token, password );
}
这段代码可能会按预期工作。作为在以下位置传递用户引用的解决方法:
async Task ResetPasswordAsync (ApplicationUser currentUser)
{
var user = await UserManager.FindByIdAsync(currentUser.UserId);
var password = CreatePassword();
var token = await UserManager.GeneratePasswordResetTokenAsync(user);
await userManager.ResetPasswordAsync(user, token, password );
}
本质上使用
UserManager
及其底层 DbContext
实例所熟悉的 User 实例引用,而不是分离的引用。
将分离用户传递到方法中的问题在于,虽然您可以将该分离引用传递给像
GeneratePasswordResetToken
这样的方法,但 UserManager
很可能会使用该 User 引用创建一个 Token 实例。 UserManager
的 DbContext
可能已加载并且仍在跟踪对用户的引用,因此当您使用该分离引用保存用户/令牌时,最终可能会收到底层 DbContext
的错误
正在跟踪参考。
简而言之,为了避免此类问题,请设计避免将实体传递到超出读取它们的
DbContext
范围之外。对于 Blazor 和 WPF 应用程序来说,这可能会很棘手,因为在使用注入的 DbContext
实例时,边界没有明确定义。如果您确实有理由使用分离实体,那么您必须小心检查它们是否被底层 DbContex
t 跟踪,如果是,则替换这些引用。解决方案是 not DbContextFactory
,因为它肯定会返回一个新的 DbContext
实例,您需要 DbContext
引用,在这种情况下 UserManager
将配置为使用。 (注入到 UserManager
中的内容)因此,在这种情况下,更快的修复方法是从 UserManager
中获取用户引用。