插入后获取实体导航属性

时间:2014-10-28 14:03:33

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

我有以下两个班级:

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和映射。

现在假设我创建了一个奖励实体并将其插入:

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约束很开心。在此插入之后,我的奖励实体具有新的Identity Id集。现在问题是奖励仍然只是我创建的奖励实体。有了这个,reward.Campaign属性为null。似乎EF将插入的实体保留在内存中,然后当我执行.SingleOrDefault(a =&gt; a.Id == reward.Id)时,它只返回内存中的实体,而不是新的代理。这可能是一件好事。

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

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

6 个答案:

答案 0 :(得分:61)

如果我理解正确,那么在通过外键财产建立关系后,你会急切地加载复杂的财产。

SaveChanges()在加载复杂属性方面没有做任何事情。最多,如果你要添加新对象,它将设置你的主键属性。

你的专栏reward = context.Set<Reward>().SingleOrDefault(a => 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();
    
  3. 手动Include() Campaign属性

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

    虽然,我建议Load,因为你已经在内存中reward了。

  4. 查看this msdn doc上的“加载相关对象”部分以获取更多信息。

答案 1 :(得分:9)

当您将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;

答案 2 :(得分:1)

您尝试使用Include()了吗?像这样:

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

答案 3 :(得分:1)

我有一个解决问题的简单方法。

而不是将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 :(得分:0)

除了 Carrie Kendall DavidG (在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 :(得分:0)

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

所以我建议如果内存没关系,请在添加记录后创建一个新的上下文对象,并改用它