实体框架 - 连接新添加的 Poco 实体并加载子对象(插入/添加)

Posted

技术标签:

【中文标题】实体框架 - 连接新添加的 Poco 实体并加载子对象(插入/添加)【英文标题】:Entity Framework - Connect Newly Added Poco Entity And Load Child Objects (Insert/Add) 【发布时间】:2013-06-29 00:25:34 【问题描述】:

我想知道是否可以连接新创建的(poco)实体添加到 DbContext(以便子对象可以在导航时延迟加载)

var user = new User();
user.LocationID = 3;
dbContext.Users.Add(user);
var locationName = user.Location.Name;

(注意:我明白我可以直接获取Location对象并赋值,上面的例子只是为了这个问题的目的)

(注二:我可以创建一个代理对象然后复制值,这样的对象将被连接(并进行延迟加载)但任何复杂的子对象(其他 pocos)都不会包含在副本中)

有没有办法连接(或加载)添加的 poco?

【问题讨论】:

这很有趣,我很惊讶上面的方法不起作用(因为你是延迟加载)也许你需要先保存更改? @LukeMcGregor 是的,一旦您 SaveChanges 对象已连接。但是我正在尝试在保存之前使用该对象。 这只是推测,但可能是由于验证,如果您实际上没有将该行提交到数据库,您实际上并不知道 FK 是否存在。 @LukeMcGregor 是有道理的,但是当在同一场景中使用新代理(而不是 poco)时,子属性会延迟加载(即使对于新实体) 【参考方案1】:

简短的回答是,在使用外键和 POCO 时,您可以在设置 FK 后显式延迟加载引用属性,如下所示:

context.Entry(e).Reference(propName).Load();

如果您需要经常遍历对象图,那么值得考虑的是,在概念级别上工作而不是使用外键属性对您来说更容易。

当上下文跟踪由导航属性表示的实体时,可以在 DetectChanges() 上同步 FK 和导航属性。 documentation 表明这仅在对象具有永久键时发生。

如果参考处于添加状态(在本例中,课程 对象),参考导航属性将不同步 使用新对象的键值,直到调用 SaveChanges。 同步不会发生,因为对象上下文没有 包含已添加对象的永久密钥,直到它们被保存。

但是,文档似乎不正确或具有误导性,当使用处于Added 状态且没有永久键的新添加实体的属性时,导航属性和外键的同步仍然会发生。

下面是我用来调查此问题的我自己的数据模型上的一些测试代码。这是将 EF5 与 .Net4 一起使用,据我所知实际上是 EF4.3。

using (MyContext context = new MyContext())

  /*
   * Uncomment these lines to test assigning FK Id when entity is being tracked
   */
  Customer cust1 = context.Customers.OrderBy(x => x.Id).First();
  Customer cust2 = context.Customers.OrderBy(x => x.Id).Skip(1).First();

  /*
   * Uncomment these lines to test assigning FK Id 
   * without having entity loaded in ObjectStateManager
   */
  //Customer cust1 = context.Customers.AsNoTracking().OrderBy(x => x.Id).First();
  //Customer cust2 = context.Customers.AsNoTracking()
  //                         .OrderBy(x => x.Id).Skip(1).First();

  //new entities
  Quote proxyQ = context.Quotes.Create();
  Quote pocoQ = new Quote();

  /*
   * if adding the new entities to context before setting FK properties
   * DetectChanges must be called later to attempt sync with nav props
   */
  context.Quotes.Add(proxyQ);
  context.Quotes.Add(pocoQ);

  //set FK Customer ids
  proxyQ.CustomerId = cust1.Id;
  pocoQ.CustomerId = cust2.Id;

  /*
   * FK / nav prop sync happens on DetectChanges() if the Customer 
   * entity is being tracked
   * it must be explicitly called if it has not been called using 
   * one of the AutoDetectChanges functions in order to sync
   */
  context.ChangeTracker.DetectChanges();

  /*
   * Alternatively, if the new entities are added to context after setting FK props
   * and AutoDetectChanges is enabled then DetectChanges is called implicitly and
   * FK / nav prop sync will happen here if the matching Customer entity is 
   * being tracked by the context.
   */
  //context.Quotes.Add(proxyQ); 
  //context.Quotes.Add(pocoQ);

  /*
   * If assigning FK Id and the entity the id represents is not tracked then 
   * proxy will lazy load Customer here.
   */
  Console.WriteLine("Proxy quote with key 0 linked to customer with name 1",
   proxyQ.Id.ToString(), proxyQ.Customer != null ? proxyQ.Customer.Name : "null");

  /*
   * Obviously no lazy loading of Customer can occur here for a POCO
   */
  Console.WriteLine("POCO quote with key 0 linked to customer with name 1",
   pocoQ.Id.ToString(), pocoQ.Customer != null ? pocoQ.Customer.Name : "null");

  /*
   * But we can explicit lazy load when using POCO if we have assigned 
   * an FK Customer Id to a POCO quote and the Customer entity is not 
   * being tracked by context
   */
   //context.Entry(pocoQ).Reference("Customer").Load();
   //Console.WriteLine("POCO quote with key 0 linked to customer with name 1",
   //  pocoQ.Id.ToString(), pocoQ.Customer != null ? pocoQ.Customer.Name : "null");

【讨论】:

感谢您的链接,但是当您创建一个新的代理 (_dbContext.Users.Create()) 时,它还没有永久密钥,子对象是延迟加载的。 ..所以这似乎不是钥匙的问题 @AndrejKovacik,我对我的数据模型做了一些实验。见编辑。 感谢您的额外努力 (+1)。我实际上使用的是 .NET 4.5 (EF5),我有点惊讶这会表现得不同,但 DetectChanges 在我的情况下不会同步 Pocos 的属性(我必须做更多的测试,看看它是否是不同的版本或我的代码中的特定内容) 再次查看您的代码,我看到 [Customer] 已加载到上下文(第一行)中,因此不需要延迟加载,因此仅在上下文中的对象之间进行同步。 (我认为如果您删除该行并将 .CustomerId = cust.Id 更改为 .CustomerId = 1;结果会有所不同) @AndrejKovacik,你是对的。我对测试 FK 到导航道具同步感到很兴奋。我已经稍微更改了我的测试代码以使其更适用于您的问题。如果内存中没有 FK 表示的对象,则可以使用 POCO 显式延迟加载,如下所示:context.Entry(e).Reference(propName).Load();

以上是关于实体框架 - 连接新添加的 Poco 实体并加载子对象(插入/添加)的主要内容,如果未能解决你的问题,请参考以下文章

将 POCO/实体添加到 DbContext 以进行自定义查询/过程,而无需先在实体框架代码中创建表

没有行为的实体框架 POCO - 需要重新设计以消除代码异味

实体框架中的 POCO 是啥? [关闭]

WCF 和实体框架 4.1 POCO

IQueryable 实体框架 POCO 映射

实体框架代码优先 - 通过主键将子实体添加到父实体