【问题标题】:Get entity navigation properties after insert插入后获取实体导航属性
【发布时间】:2014-12-23 23:25:27
【问题描述】:

我有以下两个课程:

public class Reward 
{
    public int Id { get; set; }
    public int CampaignId { get; set;
    public virtual Campaign Campaign { get; set; }
}

public class Campaign 
{
    public int Id { get; set; }
    public virtual ICollection<Reward> Rewards { get; set; }
}

有了这个,我就有了所有明显的必要东西,比如 DbContext 和映射。

现在假设我创建了一个 Reward 实体并像这样插入它:

var reward = new Reward { CampaignId = 1 };
context.Set<Reward>().Add(reward);
context.SaveChanges();

reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
//reward.Campaign is null

我显然有一个 ID 为 1 的广告系列,所以 FK 约束是满意的。在此插入之后,我的奖励实体设置了新的身份 ID。现在的问题是,奖励仍然只是我创建的奖励实体。有了这个,reward.Campaign 属性为空。似乎 EF 将插入的实体保存在内存中,然后当我执行 .SingleOrDefault(a => a.Id == reward.Id) 时,它只是返回内存中的实体,而不是新的代理。这可能是件好事。

所以问题是:如何在插入后访问或加载导航属性,或者获取一个新的代理,该代理也具有导航属性作为代理。

我可能以错误的方式插入?

【问题讨论】:

标签: c# entity-framework entity-framework-6


【解决方案1】:

如果我理解正确,您是在通过外键属性建立关系后急切地加载复杂属性。

SaveChanges() 不会以加载复杂属性的方式做任何事情。如果您要添加新对象,它最多会设置您的主键属性。

您的电话reward = context.Set&lt;Reward&gt;().SingleOrDefault(a =&gt; a.Id == reward.Id); 在加载Campaign 方面也没有任何作用,因为您的奖励对象未附加到上下文。您需要明确告诉 EF 加载该复杂对象或附加它,然后让延迟加载发挥其魔力。

所以,在你 context.SaveChanges(); 之后,你有三个选项来加载 reward.Campaign

  1. Attach() 奖励上下文,以便 Campaign 可以延迟加载(访问时加载)

     context.Rewards.Attach(reward);
    

    注意:您将只能在上下文范围内延迟加载reward.Campaign,因此如果您不打算在上下文生命周期内访问任何属性,请使用选项 2 或 3。

  2. 手动Load() Campaign 属性

     context.Entry(reward).Reference(c => c.Campaign).Load();
    

    或者如果Campaign 是一个集合,例如Campaigns

     context.Entry(reward).Collection(c => c.Campaigns).Load();
    
  3. 手动Include() Campaign 属性

     reward = context.Rewards.Include("Campaigns")
         .SingleOrDefault(r => r.Id == reward.Id);
    

    不过,我建议您使用 Load,因为您的内存中已经有 reward

查看this msdn doc 上的加载相关对象部分了解更多信息。

【讨论】:

  • 第二个选项对我来说也是最好的。
  • 第二个作品! _db.Entry(product).Reference(b => b.Category).Load();最后,它将类别信息与产品一起加载。谢谢!
  • 参考扩展在哪个程序集中? context.Entry(reward).Reference(c => c.Campaign).Load();我看到的 Reference() 方法只将导航属性名称作为字符串。
  • 想到我会提到它,因为我遇到了这个问题:当使用 .Load() 作为集合的导航属性时,你需要调用 .Collection() 而不是 .Reference() 否则你会出现运行时错误。
  • 当我想加载相关实体或另一个相关实体的集合时,第二个选项如何工作,例如Campaigns
【解决方案2】:

当您将reward 对象创建为new Reward() 时,EF 没有代理。相反,像这样使用 DbSet.Create 创建它:

var reward = context.Set<Reward>().Create();
reward.CampaignId = 5;
context.SaveChanges();

接下来将其附加到您的 DbSet:

context.Rewards.Attach(reward);

最后,您现在可以使用延迟加载来获取相关实体:

var campaign = reward.Campaign;

【讨论】:

    【解决方案3】:

    我有一个简单的解决方案。

    不是将 CampaignID 添加到奖励中,而是添加活动对象.. 所以:

    var _campaign = context.Campaign.First(c=>c.Id == 1);//how ever you get the '1'
    var reward = new Reward { Campaign = _campaign };
    context.Set<Reward>().Add(reward);
    context.SaveChanges();
    
    //reward.Campaign is not null
    

    实体框架在这里完成所有繁重的工作。

    您可能认为加载整个 Campaign 对象是一种浪费,但如果您要使用它(从它的外观来看,您似乎是这样),那么我不明白为什么不这样做。 如果您需要从 Campaign 对象访问导航属性,您甚至可以在上面获取它时使用 include 语句...

    var _campaign = context.Campaign.include(/*what ever you may require*/).First(c=>c.Id = 1);
    

    【讨论】:

      【解决方案4】:

      除了 Carrie KendallDavidG(在 VB.NET 中):

      Dim db As New MyEntities
      Dim r As Reward = = db.Set(Of Reward)().Create()
      r.CampaignId = 5
      db.Reward.Add(r) ' Here was my problem, I was not adding to database and child did not load
      db.SaveChanges()
      

      那么,r.Campaign 属性可用

      【讨论】:

        【解决方案5】:

        您是否尝试过使用Include()?像这样的:

        reward = context.Set<Reward>().Include("Campaigns").SingleOrDefault(a => a.Id == reward.Id);
        

        【讨论】:

        • 我没有尝试过,但我肯定启用了延迟加载,并且一切正常,除了首先将实体插入数据库的情况。即使这样可行,它也会破坏代码的一致性。基本上我必须在插入后这样做,但在其他情况下我不需要它。进行分离更加一致,因为我可以在 SaveChanges 之后立即进行。我不知道下一次何时需要选择数据,何时真正需要该 Include 以及何时不需要它。您可以说,每次都这样做,但为什么我在这种情况下启用了延迟加载?
        【解决方案6】:

        如果您有多个导航属性或想要添加多个记录,则可能很难通过这些方式进行。

        所以我建议如果内存无关紧要在添加记录后创建一个新的上下文对象,然后使用它来代替

        【讨论】:

        • 是的,我还提到如果我在另一个上下文中进行查询,一切正常,但是分离实体方法呢?为什么它在一种情况下有效,为什么在另一种情况下抛出异常?也许这整个问题,返回导航属性为空,应该作为一个错误提交给实体开发团队?它看起来像一个错误。 SaveChanges 后实体跟踪中断。我相信它应该发送查询并检索数据,但看起来对象被认为是完整的。
        猜你喜欢
        • 2015-03-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多