无法更新实体框架虚拟列表对象

本文关键字:虚拟 列表 对象 框架 实体 更新 | 更新日期: 2023-09-27 18:36:41

我将尝试简单地解释我的实体框架模型。我有一个用户对象,它有一个零个或多个用户兴趣对象的集合。每个用户兴趣对象只有三个属性:唯一 ID、用户 ID 和描述。

每当用户更新 User 对象时,它也应该更新相关的 UserInterest 对象,但由于这些对象是自由格式的(即不是允许的兴趣列表的一部分),我希望用户将类型为"字符串"的列表传递给 webmethod 他们所有兴趣的名称。理想情况下,代码将查看用户现有的兴趣列表,删除任何不再相关的兴趣列表并添加新的兴趣列表,并保留已经存在的兴趣列表。

我的对象模型定义

[Table("User")]
public class DbUser {
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public virtual IList<DbUserInterest> Interests { get; set; }
}
[Table("UserInterest")]
public class DbUserInterest : IEntityComparable<DbUserInterest>
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long Id { get; set; }
    public virtual DbUser User { get; set; }
    public int? UserId { get; set; }
}

上下文 流畅映射

modelBuilder.Entity<DbUser>()
    .HasKey(u => u.UserId);
modelBuilder.Entity<DbUser>()
    .Property(u => u.UserId)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<DbUser>()
    .HasMany(u => u.Interests)
    .WithRequired(p => p.User)
    .WillCascadeOnDelete(true);
modelBuilder.Entity<DbUserInterest>()
    .HasKey(p => p.Id);
modelBuilder.Entity<DbUserInterest>()
    .Property(p => p.Id)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<DbUserInterest>()
    .HasRequired(p => p.User)
    .WithMany(u => u.Interests)
    .WillCascadeOnDelete(true);

最后,我的网络方法代码和存储库方法来进行更新

public UpdateUserProfileDetailsResponse UpdateUserProfileDetails(UpdateUserProfileDetailsRequest request)
{
    try
    {
        var dbItem = _userDataRepository.GetItem(request.Header.UserId);
        dbItem.Interests.Clear();
        foreach (var dbInterest in request.UserInterests)
            dbItem.Interests.Add(new DbUserInterest { Name = dbInterest, UserId = dbItem.UserId});
        _userDataRepository.UpdateItem(dbItem);
        _userDataRepository.Save();
    }
    catch (Exception ex)
    {
    }
}
public override bool UpdateItem(DbUser item)
{
    var dbItem = GetItem(item.UserId);
    if (dbItem == null)
        throw new DataRepositoryException("User not found to update", "UserDataRepository.UpdateItem");
    var dbInterests = Work.Context.UserInterests.Where(b => b.UserId == item.UserId).ToList();
    var interestsToRemove = (from interest in dbInterests let found = item.Interests.Any(p => p.IsSame(interest)) where !found select interest).ToList();
    var interestsToAdd = (from interest in item.Interests let found = dbInterests.Any(p => p.IsSame(interest)) where !found select interest).ToList();
    foreach (var interest in interestsToRemove)
        Work.Context.UserInterests.Remove(interest);
    foreach (var interest in interestsToAdd)
    {
        interest.UserId = item.UserId;
        Work.Context.UserInterests.Add(interest);
    }                
    Work.Context.Entry(dbItem).State = EntityState.Modified;
    return Work.Context.Entry(dbItem).GetValidationResult().IsValid;
}

当我运行它时,在Repository.Save()行出现异常

Assert.IsTrue failed. An unexpected error occurred: An error occurred while updating the entries. See the inner exception for details.

但有趣的是,在 webmethod 中,如果我注释掉该行dbItem.Interests.Clear();它不会引发错误,尽管您当然会得到重复项或额外的项目,因为它认为一切都是要添加的新兴趣。但是,删除此行是我可以让代码不出错的唯一方法

以前,我将 Interest 对象的 UserId 属性设置为不可空,然后错误略有不同,关于您无法更改不可为空的外键实体的关系,这就是为什么我将属性更改为可为空但仍然没有通过。

有什么想法吗?

无法更新实体框架虚拟列表对象

不能只是清除集合,然后尝试重建它。EF 不是这样工作的。DbContext在其更改跟踪器中跟踪从数据库带回的所有对象。按照自己的方式执行此操作当然会导致重复项,因为 EF 看到它们根本不在更改跟踪器中,因此它们必须是全新的对象,需要添加到数据库中。

您需要在UpdateUserProfileDetails方法中执行添加/删除逻辑,否则必须找到一种将request.UserInterests传递到UpdateItem方法的方法。因为需要调整现有实体,而不是请求中找到的实体(EF 认为这是新的实体)。

你可以这样尝试删除

dbItem.Interests.Clear();

然后

    foreach (var dbInterest in request.UserInterests){
      if(dbItem.Interests.Any()){
        if (dbItem.Interests.Count(i=> i.Name==dbInterest && i.UserId==dbItem.UserId) == 0){
          dbItem.Interests.Add(new DbUserInterest { Name = dbInterest, UserId = dbItem.UserId});
        }
      }
   }