如何更新多对多关系中的现有对象(.Net 5)

Posted

技术标签:

【中文标题】如何更新多对多关系中的现有对象(.Net 5)【英文标题】:How to update an existing object in a many to many relationship (.Net 5) 【发布时间】:2021-03-02 16:01:39 【问题描述】:

我一直在将 .Net 5 和 EF Core 5 用于小型 Web 应用程序。鉴于 EF Core 5 支持许多开箱即用,因此不需要连接表。

我在更新数据库中已存在的对象时遇到了问题。对于我的应用,我有运动员和父母,它们之间存在多对多关系。

public class Athlete

    public int Id  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 
    public string MiddleName  get; set; 
    public string Email  get; set; 
    public string ContactNumber  get; set; 
    public string Street  get; set; 
    public int Postcode  get; set; 
    public string City  get; set; 
    public StateEnum State  get; set; 
    public DateTime DateofBirth  get; set; 
    public DateTime DateSignedUp get; set;
    public virtual ICollection<Parent> Parents  get; set; 

public class Parent

    public int Id  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 
    public string MiddleName  get; set; 
    public string Email  get; set; 
    public string ContactNumber  get; set; 
    public string Street  get; set; 
    public int Postcode  get; set; 
    public string City  get; set; 
    public StateEnum State  get; set; 
    public DateTime DateofBirth  get; set; 
    public DateTime DateSignedUp get; set;
    public virtual ICollection<Athlete> Athletes  get; set; 

当我尝试更新与另外两个父母有关系的现有运动员时,我收到一个错误:

违反主键约束“PK_AthleteParent”。无法插入 对象“dbo.AthleteParent”中的重复键。重复键值 是 (31, 1)

[HttpPost]
public async Task<ActionResult<Athlete>> PostAthlete(Athlete athlete)

     _context.Athletes.Update(athlete);
     await _context.SaveChangesAsync();
     return Ok(athlete));

据我所知,当实体尝试更新我的 Athlete 时,它​​会尝试将新行插入到连接表中,即使父项已经存在于连接表中。有没有办法让实体在更新关系时删除任何记录?或者有没有办法告诉实体更新加入表以匹配传入的运动员对象?

【问题讨论】:

你会认为当微软发布many-many support时,他们已经有了更新这样的基本功能 我还没有在 EF 核心 5 中查看多对多,但可能与 EF 中的多对多一样。您必须自己弄清楚哪些关系是新的或应该删除的。 EF 不知道此断开连接方案中的现有关系。通常删除所有旧关系并插入当前关系就足够了。 嗯,那么在这种情况下,我最好不要使用自动连接表,对吧?因为目前我无法访问它,因为实体应该默认处理它。我想如果我自己维护 AthletesParents 表,那么根据需要删除/添加记录会很容易。 国际海事组织,是的。如果没有明确的联结类,我从来不喜欢这些多对多的关联。不仅因为这里提到的摩擦,而且主要是因为它们的表达力不足以涵盖关联类的概念 @Liam Junction 表纯粹是一个以数据库为中心的概念。从概念上讲,您的域模型甚至不应该知道该实体,并且您自己也不应该操纵它。当您使用 ORM(EF Core)时,ORM 有责任处理它。这就是最终在 EF 5.0 中引入新的透明配置的原因。 【参考方案1】:

举个简单的例子:

public class Foo 
    Guid Id  get; set; 
    ICollection<Bar> Bars  get; set; 


public class Bar 
    Guid Id  get; set; 
    ICollection<Foo> Foos  get; set; 

您可以在跟踪的Foo 实例上调用clear(),然后重新添加您要分配的Bar 实例。我发现这是避免约束异常的好方法 - 比手动尝试找出 Bars 发生了哪些变化要容易得多。

var foo = context.Foos.Include(x => x.Bars).FirstOrDefault(x => x.Id == someGuid);
foo.Bars.Clear();
foo.Bars.Add(bar1);
foo.Bars.Add(bar2);
...
context.Update(foo);
context.SaveChanges();

【讨论】:

有趣,我不知道 Clear() 函数,但肯定胜过我一直在使用的解决方案:)。

以上是关于如何更新多对多关系中的现有对象(.Net 5)的主要内容,如果未能解决你的问题,请参考以下文章

EF Core 5,删除多对多关系

多对多关系中的缓存更新

实体框架:使用现有数据(种子)填充多对多关系

多对多关系 - 如何更新数据透视表中的属性

Laravel 5:如何更新与多对多相关的项目(删除+添加)

如何在后端创建多对多关系