【发布时间】:2021-03-11 23:33:26
【问题描述】:
我在 .NET 5.0 Web 应用程序中使用 Entity Framework Core 5。我有两个以多对多关系连接的类。如here 所述,我正在使用“直接”方法。这意味着我没有在代码中明确定义的连接表;相反,EF 已从以下架构推断出这种关系:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public ICollection<Group> Groups { get; set; }
}
public class Group
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public ICollection<User> Users { get; set; }
}
我希望能够通过简单地向 EF 提供新对象来更新“组”或“用户”。例如,如果我为 EF 提供了一个与数据库中已有的组具有相同 ID 的“组”对象,但它现在在用户集合中有一个额外的“用户”,那么我希望 EF 更新“用户组” ' 表,它在幕后制作了一条新记录来表示这种关系。
这是我尝试过的:
1.
public void Update(Group group)
{
Group oldGroup = _context.Groups
.Include(g => g.Users)
.First(g => g.ID == group.ID);
_context.Entry(oldGroup).CurrentValues.SetValues(group);
_context.SaveChanges();
}
结果:保存对属于 Group 对象的任何道具的更改,但不影响任何关系。
2.
public void Update(Group group)
{
Group oldGroup = _context.Groups
.Include(g => g.Users)
.First(g => g.ID == group.ID);
oldGroup.Users = group.Users;
_context.Entry(oldGroup).CurrentValues.SetValues(group);
_context.SaveChanges();
}
结果:异常。
System.InvalidOperationException: 'The instance of entity type 'User' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
我从here 为 Intersect 和 LeftComplementRight 添加了一些扩展方法,并尝试自己计算差异。
public void Update(Group group)
{
Group oldGroup = _context.Groups
.Include(g => g.Users)
.First(g => g.ID == group.ID);
var oldUsers = oldGroup.Users;
var newUsers = group.Users;
var toBeRemoved = oldUsers.LeftComplementRight(newUsers, x => x.ID);
var toBeAdded = newUsers.LeftComplementRight(oldUsers, x => x.ID);
var toBeUpdated = oldUsers.Intersect(newUsers, x => x.ID);
foreach (var u in toBeAdded)
{
oldGroup.Users.Add(u);
}
foreach (var u in toBeRemoved)
{
oldGroup.Users.Remove(u);
}
_context.Entry(oldGroup).CurrentValues.SetValues(group);
_context.SaveChanges();
}
结果:与上述相同的异常。 此时我意识到组成“用户”集合的“用户”对象已实例化“组”对象并填充回组对象的自引用。我意识到这一点可能是混淆了 EF,所以我尝试了这个:
4.
public void Update(Group group)
{
Group oldGroup = _context.Groups
.Include(g => g.Users)
.First(g => g.ID == group.ID);
var oldUsers = oldGroup.Users;
var newUsers = group.Users;
var toBeRemoved = oldUsers.LeftComplementRight(newUsers, x => x.ID);
var toBeAdded = newUsers.LeftComplementRight(oldUsers, x => x.ID);
var toBeUpdated = oldUsers.Intersect(newUsers, x => x.ID);
foreach (var u in toBeAdded)
{
u.Groups = null;
oldGroup.Users.Add(u);
}
foreach (var u in toBeRemoved)
{
u.Groups = null;
oldGroup.Users.Remove(u);
}
_context.Entry(oldGroup).CurrentValues.SetValues(group);
_context.SaveChanges();
}
这行得通,但似乎我做的工作太多了。我预计在整个项目中会有多个多对多关系,并且我不想在每个更新方法上都复制此代码。我想我可以创建一个扩展方法,但我觉得 EF 应该能够处理这个常见的用例。
我错过了什么吗?
【问题讨论】:
-
字数太多,看不懂你的问题。您想向组中添加新用户还是从组中删除用户?没有其他选择
-
添加或删除,或都不添加。看4下的代码。这是我想要实现的,但我正在寻找更好的方法来做到这一点。
-
这种情况很少发生,因为我喜欢简单的决定。所以我通常只是从组中删除所有旧用户,然后从新组中添加所有用户。我不在乎某些用户是否可以相交,因为这是最可靠和最简单的方法。
标签: c# entity-framework asp.net-core entity-framework-core