无法更新 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});
}
}
}
我将尝试简单地解释一下我的 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});
}
}
}