我注意到,在多对多关系中,联合表由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)。
谢谢
答案 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,
});
但是您确定还没有这样的关系吗?您将必须先获取它。并且您确定在获取和添加之间没有其他人添加该关系吗?
您看到了很多问题,只是不值得自己处理联结表!