插入后获取实体导航属性



我有以下两个类:

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约束是令人高兴的。插入后,我的奖励实体将设置新的身份Id。现在的问题是,奖励仍然只是我创建的奖励实体。这就是回报。市场活动属性为null。EF似乎将插入的实体保留在内存中,然后当我执行.SingleOrDefault(a=>a.Id==reward.Id)时,它只是返回内存中的实体,而不是新的代理。这可能是一件好事。

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

我可能插错了吗?

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

CCD_ 1不以加载复杂属性的方式执行任何操作。如果你添加新对象,它最多会设置你的主键属性。

您的线路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() SaveChanges()0属性

     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了。

有关详细信息,请查看此msdn文档的"加载相关对象"部分。

当您将reward对象创建为new Reward()时,EF没有代理。相反,使用DbSet创建它。创建方式如下:

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

接下来将其附加到您的数据库集:

context.Rewards.Attach(reward);

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

var campaign = reward.Campaign;

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

不要将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);

除了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可用

您尝试过使用Include()吗?类似这样的东西:

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

如果您有多个导航属性或想要添加多个记录,那么用这些方法可能很难做到。

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

最新更新