无法更新 Entity Framework 虚拟列表对象

Cannot update Entity Framework virtual list objects

我将尝试简单地解释一下我的 Entity Framework 模型。我有一个 User 对象,它包含零个或多个 UserInterest 对象的集合。每个用户兴趣对象只有三个属性,唯一 ID、用户 ID 和描述。

每当用户更新 User 对象时,它也应该更新相关的 UserInterest 对象,但是因为这些是自由形式的(即不是允许的兴趣列表的一部分),我希望用户传入一个类型列表"string" 到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; }
}

上下文 Fluent 映射

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);

最后是我的 webmethod 代码和存储库方法来进行更新

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 属性设置为不可为空,然后错误略有不同,关于你无法更改不可为空的外键实体的关系,这就是我更改的原因属性为 nullable 但仍然不行。

有什么想法吗?

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

您需要在 UpdateUserProfileDetails 方法中执行 add/remove 逻辑,否则您必须找到一种方法将 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});
        }
      }
   }