我将尝试简单地解释我的实体框架模型。我有一个用户对象,它的集合为零或多个UserInterest对象。每个用户兴趣对象只有三个属性,即唯一ID、用户Id和描述。
每当用户更新用户对象时,它也应该更新相关的UserInterest对象,但是由于这些对象是免费的(即不是允许兴趣列表的一部分),所以我希望用户将一个类型为"string“的列表传递给他们感兴趣的名称的all方法。理想情况下,代码将查看用户现有的兴趣列表,删除任何不再相关的内容,并添加新的兴趣列表,并保留已经存在的兴趣列表。
我的对象模型定义
[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.但有趣的是,在get方法中,如果我注释掉行dbItem.Interests.Clear();,它不会抛出一个错误,当然,您会得到重复或额外的项目,因为它认为所有内容都需要添加。但是,删除这一行是使代码不出错的唯一方法。
在此之前,我将兴趣对象的UserId属性设置为非空对象,然后错误略有不同,您不能更改不可空的外键实体的关系,这就是为什么我将属性更改为可空但仍然不为空。
有什么想法吗?
发布于 2015-01-08 14:51:57
您不能只清除集合,然后尝试重新构建它。EF不是那样工作的。DbContext在其变更跟踪器中跟踪从数据库中带回的所有对象。当然,这样做会造成重复,因为EF看到它们根本不在变更跟踪器中,所以它们必须是全新的对象,因此需要添加到数据库中。
您需要在UpdateUserProfileDetails方法中执行添加/删除逻辑,或者必须找到将request.UserInterests传递到UpdateItem方法的方法。因为您需要调整现有实体,而不是在请求中找到的实体( EF认为这些实体是新的)。
发布于 2015-01-08 14:53:54
你可以用这种方式移除
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});
}
}
}https://stackoverflow.com/questions/27842157
复制相似问题