使用实体框架更新多对多关系
本文关键字:关系 更新 框架 实体 | 更新日期: 2023-09-27 18:28:03
我有一个多对多联接表,带有一个双关键字(PersonId,RoleId)。为了简单起见,我在Person表中只有一个PersonId。此外,使用EF7,它还不支持EF6的很多优点(例如通过导航属性连接表的隐含性)。
虽然我可以在SQLite中运行这个查询,并且它可以毫无问题地运行:update PersonRole set RoleId = 2 where PersonId = 1
,但我不能在EF:中做同样的事情
var du = context.PersonsRoles.Where(p => p.PersonId == 1).First();
du.RoleId = 2;
context.PersonsRoles.Update(du);
context.SaveChanges(); //get an error here
错误是:"发生了未处理的异常:实体类型"PersonRole"的属性"RoleId"是键的一部分,因此无法修改或标记为已修改。"
(ETA根据下面的评论)-我的模型是:
public class PersonRole
{
public virtual int PersonId { get; set; }
public virtual int RoleId { get; set; }
}
我找到了一个答案,其中包括删除原始行(1,1)然后重新插入(1,2)的选项,但这对我来说似乎效率很低。这真的是修改关系的唯一方法吗?
您试图在一侧修改多对多关系的键。多对多关系在数据库中用一个表表示,该表包含关系中双方的外键。
您试图做的是尝试更改对象的键,但外键表中仍保留引用,导致违反约束,因为N-N表中的值尚未更新。
EF7中不允许进行此更改。您应该使用SQL命令来执行此操作,而不是考虑多对多表更新。
您可以使用我所做的扩展来删除未选择的,并将新选择的添加到列表中
public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
}
public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
return items
.GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
.SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
.Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
.Select(t => t.t.item);
}
使用它看起来像这个
var model = db.Persons
.Include(x => x.PersonsRoles)
.FirstOrDefault(x => x.PersonId == person.PersonId );
db.TryUpdateManyToMany(model.PersonsRoles, listOfNewRoleIds
.Select(x => new PersonRole
{
RoleId = x,
PersonId = person.PersonId
}), x => x.PersonId);