Entity Framework 维护多个插入的外键
Entity Framework maintain foreign key for multiple inserts
我继承了一个包含 3 个表的简单数据库。
董事会Table:董事会ID,董事会名称
城市 Table:CityID、CityName、BoardID
Child Table: ChildID, Child姓名, CityID, BoardID
所有 ID 字段都已打开标识插入。
当我插入时,
我先插入 Board,保存更改,将 ID 插入 City Table,保存更改,最后将两个 ID 插入 Child Table 并保存更改。
有没有办法在上下文中维护它并立即保存更改?
是的,首先阅读导航属性和与 EF 的映射关系。使用这些关系,EF 将跟踪哪些实体相互依赖,以便您创建实体图,例如:
using ( var context = new MyContext())
{
var newBoard = new Board { BoardName = boardName };
var newCity = new City { CityName = cityName, Board = newBoard };
var newChild = new Child { ChildName = childName, City = newCity };
context.Children.Add(newChild);
context.SaveChanges();
}
当 EF 提交更改时,每个映射到身份列的实体都将获得它的新 ID,这些 ID 将自动分配给关联实体的 FK。
使用 EF,您也不需要在实体中公开 FK 字段,建议避免公开 FK。通过 EF Core,您可以使用影子属性来映射您的实体。公开 FK 会导致更新时出现问题。如果 child 公开了一个城市引用和一个 CityId FK 属性 并且我想为 child 分配一个新城市,我现在有两种方法:
child.City = fourthCity;
// or
child.CityId = 4;
这可能会导致不一致的行为和围绕错误假设的错误。例如:
var child = context.Children.Find(1); // Child #1 references City #1.
var fourthCity = context.Cities.Find(4);
child.City = fourthCity;
Console.WriteLine(child.City.CityId); // "4"
Console.WriteLine(child.CityId); // "1"
context.SaveChanges();
Console.WriteLine(child.CityId); // Now it's "4"
任何在 SaveChanges
之前引用 FK 的代码都将获得旧 ID,而引用相关实体 ID 的代码将获得新 ID。 FK 在调用 SaveChanges
之前不会更新。
更改 FK 也会导致问题和错误..
var child = context.Children.Find(1); // Child #1 references City #1.
child.CityId = 4;
Console.WriteLine(child.CityId); // "4"
Console.WriteLine(child.City.CityId); // NullReferenceException! (Lazy load call fails)
延迟加载(至少在 EF6 中)如果您尝试执行此操作,则会出现抖动。如果你急于加载城市:
var child = context.Children.Include(x => x.City).Single(x => x.ChildId == 1); // Child #1 references City #1.
child.CityId = 4;
Console.WriteLine(child.CityId); // "4"
Console.WriteLine(child.City.CityId); // "1"
Context.SaveChanges();
Console.WriteLine(child.City.CityId); // "4"
您将遇到代码引用 FK 与导航属性的不一致行为。在某些情况下,您可能希望实体公开 FK 而不是导航属性(例如在执行大量操作时),因此我建议使用其中之一,不要同时使用两者。
此外,请避免对您的模式进行反规范化。如果 Child 与城市相关联,而城市与董事会相关联,则您可以通过城市引用 child 的董事会,而不是在 child 上使用 BoardID。
即child.City.Board.BoardName
非规范化是有问题的,因为没有办法强制 child 的董事会参考将匹配它指定的城市董事会参考。
例如:
var board1 = context.Boards.Find(1);
var board2 = context.Boards.Find(2);
var child = context.Children.Find(1);
如果 Child Id #1 引用 City Id #1 引用 Board Id #1,并且 Child 也引用 Board Id #1,那么代码中类似这样的内容,有意或错误可能会导致未来出现问题:
child.Board = board2;
通过 child.Board.BoardId
引用 child 的任何代码都会 return “2”,但是任何引用 child.City.Board.BoardId
的代码仍然会得到 return “1”。与在您的实体上公开 FK 的问题类似,这些非规范化引用可能会导致错误引用,具体取决于您查看的位置,除了与 FK 问题不同的是,这些在提交更改时不会 auto-resolve。 child 的董事会 ID 与其引用城市的董事会 ID 在数据中不同步。
我继承了一个包含 3 个表的简单数据库。
董事会Table:董事会ID,董事会名称 城市 Table:CityID、CityName、BoardID Child Table: ChildID, Child姓名, CityID, BoardID
所有 ID 字段都已打开标识插入。
当我插入时,
我先插入 Board,保存更改,将 ID 插入 City Table,保存更改,最后将两个 ID 插入 Child Table 并保存更改。
有没有办法在上下文中维护它并立即保存更改?
是的,首先阅读导航属性和与 EF 的映射关系。使用这些关系,EF 将跟踪哪些实体相互依赖,以便您创建实体图,例如:
using ( var context = new MyContext())
{
var newBoard = new Board { BoardName = boardName };
var newCity = new City { CityName = cityName, Board = newBoard };
var newChild = new Child { ChildName = childName, City = newCity };
context.Children.Add(newChild);
context.SaveChanges();
}
当 EF 提交更改时,每个映射到身份列的实体都将获得它的新 ID,这些 ID 将自动分配给关联实体的 FK。
使用 EF,您也不需要在实体中公开 FK 字段,建议避免公开 FK。通过 EF Core,您可以使用影子属性来映射您的实体。公开 FK 会导致更新时出现问题。如果 child 公开了一个城市引用和一个 CityId FK 属性 并且我想为 child 分配一个新城市,我现在有两种方法:
child.City = fourthCity;
// or
child.CityId = 4;
这可能会导致不一致的行为和围绕错误假设的错误。例如:
var child = context.Children.Find(1); // Child #1 references City #1.
var fourthCity = context.Cities.Find(4);
child.City = fourthCity;
Console.WriteLine(child.City.CityId); // "4"
Console.WriteLine(child.CityId); // "1"
context.SaveChanges();
Console.WriteLine(child.CityId); // Now it's "4"
任何在 SaveChanges
之前引用 FK 的代码都将获得旧 ID,而引用相关实体 ID 的代码将获得新 ID。 FK 在调用 SaveChanges
之前不会更新。
更改 FK 也会导致问题和错误..
var child = context.Children.Find(1); // Child #1 references City #1.
child.CityId = 4;
Console.WriteLine(child.CityId); // "4"
Console.WriteLine(child.City.CityId); // NullReferenceException! (Lazy load call fails)
延迟加载(至少在 EF6 中)如果您尝试执行此操作,则会出现抖动。如果你急于加载城市:
var child = context.Children.Include(x => x.City).Single(x => x.ChildId == 1); // Child #1 references City #1.
child.CityId = 4;
Console.WriteLine(child.CityId); // "4"
Console.WriteLine(child.City.CityId); // "1"
Context.SaveChanges();
Console.WriteLine(child.City.CityId); // "4"
您将遇到代码引用 FK 与导航属性的不一致行为。在某些情况下,您可能希望实体公开 FK 而不是导航属性(例如在执行大量操作时),因此我建议使用其中之一,不要同时使用两者。
此外,请避免对您的模式进行反规范化。如果 Child 与城市相关联,而城市与董事会相关联,则您可以通过城市引用 child 的董事会,而不是在 child 上使用 BoardID。
即child.City.Board.BoardName
非规范化是有问题的,因为没有办法强制 child 的董事会参考将匹配它指定的城市董事会参考。
例如:
var board1 = context.Boards.Find(1);
var board2 = context.Boards.Find(2);
var child = context.Children.Find(1);
如果 Child Id #1 引用 City Id #1 引用 Board Id #1,并且 Child 也引用 Board Id #1,那么代码中类似这样的内容,有意或错误可能会导致未来出现问题:
child.Board = board2;
通过 child.Board.BoardId
引用 child 的任何代码都会 return “2”,但是任何引用 child.City.Board.BoardId
的代码仍然会得到 return “1”。与在您的实体上公开 FK 的问题类似,这些非规范化引用可能会导致错误引用,具体取决于您查看的位置,除了与 FK 问题不同的是,这些在提交更改时不会 auto-resolve。 child 的董事会 ID 与其引用城市的董事会 ID 在数据中不同步。