我在带有 Identity 的 Blazor Web 应用程序(在 .NET 9 上)中针对 SQL Server 数据库使用 EF Core,并使用 AutoMapper 在数据库实体和从 API 发送到 API 的 DTO 之间进行转换。
我有由以下(简化)类表示的 ASP.NET Core Identity 用户:
public class User : IdentityUser
{
public int? CompanyId { get; set; }
public virtual Company? Company { get; set; }
// Other properties removed for clarity
}
用户可以与公司关联,也可以不与公司关联。
Company
类(再次简化)如下所示:
public class Company
{
public int Id { get; set; }
public virtual ObservableCollection<User> Users { get; set; } = [];
}
我使用 AutoMapper 从数据库实体创建 DTO。 DTO 从 API 发出,新的/修改的 DTO 由 API 接收,转换为实体并保存到数据库。
DTO类基本上反映了实体,所以我不会在这里展示它们。
我的映射器配置如下...
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Company, CompanyDto>()
.ReverseMap();
CreateMap<User, UserDto>()
.ReverseMap()
// Next three rules to account for Identity storing the email four times
.ForPath(entity => entity.NormalizedEmail, opt => opt.MapFrom(dto => dto.Email.ToUpper()))
.ForPath(entity => entity.UserName, opt => opt.MapFrom(dto => dto.Email))
.ForPath(entity => entity.NormalizedUserName, opt => opt.MapFrom(dto => dto.Email.ToUpper()));
}
}
当
CompanyDto
被发送回 API 进行保存时,我尝试按如下方式修复用户...
public async Task SaveCompany(CompanyDto company)
{
if (company.Id == 0)
{
context.Companies.Add(mapper.Map<Company>(company));
}
else
{
Company? dbCompany = await context.Companies
.Include(c => c.Users)
.SingleOrDefaultAsync(c => c.Id == company.Id);
if (dbCompany is not null)
{
mapper.Map(company, dbCompany);
foreach (UserDto user in company.Users)
{
User? dbUser = dbCompany.Users.SingleOrDefault(u => u.Id == user.ID);
if (dbUser is null)
{
dbCompany.Users.Add(mapper.Map<User>(user));
}
else
{
mapper.Map(user, dbUser);
}
context.Companies.Update(dbCompany);
}
}
}
await context.SaveChangesAsync();
}
我遇到的问题是,每当我尝试保存具有任何关联用户的公司时,我都会遇到异常:
无法跟踪实体类型“User”的实例,因为已跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
即使公司或与其关联的用户没有任何变化,也会发生这种情况。
我添加了以下代码来查看上下文中的内容,但没有输出任何内容,这意味着上下文尚未跟踪任何内容...
context.ChangeTracker
.Entries()
.Where(x => includeUnchanged || x.State != EntityState.Unchanged)
.OrderBy(x => x.Entity.GetType().Name)
.ForEach(x => {
Console.WriteLine($"Entity ({x.Entity.GetType().Name}) has been {x.State.ToString()}");
});
有人能解释我做错了什么吗?谢谢
我认为该错误可能是由context.Update引起的。理论上,跟踪器在保存项目时应该能够自行识别。尝试删除更新并仅保留
SaveChangesAsync
。