自动添加/删除联合表中的行

时间:2019-04-11 06:22:15

标签: c# asp.net-mvc entity-framework linq

我注意到,在多对多关系中,联合表由ef维护。即

Center 
ID     name
1      C1
2      C2

Facility
ID     Name
1      F1
2      F2

Center_Facitliy
CenterID    FacilityID
1             1
1             2
2             1
2             2

如果我这样做

Center = GetCenterById(2);Indcluds Center_Facilities collection
Center.Center_Facilities = new List<Center_Facility> { 
                               new Center_Facility { FacilityID = 3 }, 
                               new Center_Facility { FacilityID = 1}    
                            }

将此Center_Facility表更新为

Center_Facitliy
CenterID    FacilityID
1             1
1             2
2             1
2             3

它会自动删除2-2,并添加2-3。有人可以解释为什么以及如何发生吗?

以下情况不会发生

CenterEmployee
ID   Name     Phone    CenterID
1    E1       123       1
2    E2       123       1
3    E3       123       2

(员工只能在一个中心工作)

Center = GetCenterById(1); //Indcluds CenterEmployee collection
Center.CenterEmployee = new List<CenterEmployee> { 
                               new CenterEmployee { Name=E5, Phone=123 }, 
                               new CenterEmployee { Name=E4, phone=123 }    
                            }
Result
CenterEmployee
ID   Name     Phone    CenterID
1    E1       123       null
2    E2       123       null
3    E3       123       2
4    E4       123       1
5    E5       123       1

它更新了FK关系,但没有删除旧记录(1,2)。

谢谢

2 个答案:

答案 0 :(得分:2)

按以下方式更新Center.Center_Facilities时:

Center.Center_Facilities = new List<Center_Facility> { 
                               new Center_Facility { FacilityID = 3 }, 
                               new Center_Facility { FacilityID = 1}    
                            }

这意味着您要用新的Center_Facilities集合替换Center中现有的Center_Facilities(已加载到上下文中)集合,这就是为什么要删除先前的集合以及正在添加新收藏。这是Entity Framework的预期行为,否则,如果您的新集合包含数据库中已经存在的一组Center_Facility,那么它将抛出重复的键异常。

此外,现在您认为您不想替换而是要添加到现有的Center_Facilities集合中,那么您必须执行以下操作:

Center.Center_Facilities.AddRange(new List<Center_Facility> { 
                                   new Center_Facility { FacilityID = 3 }, 
                                   new Center_Facility { FacilityID = 1}    
                                })

在这种情况下,您必须确保新添加的集合不包含数据库中已经存在的任何Center_Facility集。

希望现在对您很清楚。

答案 1 :(得分:2)

删除[2,2]和[2,3]的原因,是因为您刚刚告诉dbContext中心[2]仅具有ID为[1]和[3]的中心设施。 / p>

是的,关系数据库管理系统使用单独的表:联结表实现了多对多关系。通常,联结表仅包含已连接项的ID。由于[CenterId,FacilityId]组合是唯一的,因此该组合通常用作主键,这使得查询起来确实非常快捷。

如果首先在实体框架代码中设计多对多关系,则不必提及联结表:

class Center
{
    public int Id {get; set;}
    ...

    // every Center offers zero or more facilities (many-to-many)
    public virtual ICollection<Factility> Facilities {get; set;}
}
class Facility
{
    public int Id {get; set;}
    ...

    // every Facility is offered at zero or more Centers (many-to-many)
    public virtual ICollection<Center> Centers {get; set;}
}
class MyDbContext : DbContext
{
    public DbSet<Center> Centers {get; set;}
    public DbSet<Facility> Facilities {get; set;}
}

请注意,未提及连接表!

  

在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系。

由于使用了双面虚拟ICollection,实体框架可以检测到您设计了多对多关系,并会自动为您创建合适的连接表,而无需使用属性或流畅的API。

但是如何在没有联结表的情况下进行查询?

请勿使用联结表,而应使用ICollections:

请给我英国中心的(几个属性)其过时的设施(几个属性):

var result = dbContext.Centers
    .Where(center => center.CountryCode == "UK")
    .Select(center => new
    {
        // select only the properties that you plan to use
        Id = center.Id,
        Name = center.Name,
        ...

        ObsoleteFacilities = center.Facilities
          .Where(facility => facility.Obsolete)
          .Select(facility => new
          {
              // again, only the properties you plan to use
              Id = facility.Id,
              ...
          })
          .ToList(),
     });

实体框架了解您的多对多关系,并且非常聪明,足以理解这一点,需要与联接表进行(组)联接。

现在,如果您已获取Center [2]和Facility [3],则可以轻松连接它们,而不必担心它们之间是否已经存在关系:

var center2 = dbContext.Centers.Find(2);
var facility3 = dbContext.Facilities.Find(3);
// TODO: check if really found

// connect them:
center2.Facilities.Add(facility3);

// add a new facility:
center.Facilities.Add(new Facility() {...});
dbContext.SaveChanges();

或者,您可以将中心添加到设施中:

facility3.Centers.Add(center2);
dbContext.Facilities.Add(new Facility()
{
    ...
    Centers = new Center[] {center2},
});

关于此点的好处是,除了非常直观之外,您不必担心center2和设施3之间是否已经存在关系。

缺点是您必须为此获取完整的中心和设施。通常,您查询数据的频率要比添加和更新数据的频率高得多,并且更改关系通常是在(相对较慢的)用户交互之后完成的,因此我怀疑这是否会成为问题。

但是,如果您真的想添加关系而不必先获取中心和设施,则可以添加包含联结表的DbSet

public DbSet<CenterFacility> CenterFacilities {get; set;}

要添加关系:

dbContext.CenterFunctions.Add(new CenterFacility()
{
    CenterId = 2,
    FacilityId = 3,
});

但是您确定还没有这样的关系吗?您将必须先获取它。并且您确定在获取和添加之间没有其他人添加该关系吗?

您看到了很多问题,只是不值得自己处理联结表!