我有以下两个班级:
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)时,它只返回内存中的实体,而不是新的代理。这可能是一件好事。
所以问题是:如何在插入后访问或加载导航属性,或者获取具有导航属性作为代理的新代理。
我是否可能以错误的方式插入?
答案 0 :(得分:61)
如果我理解正确,那么在通过外键财产建立关系后,你会急切地加载复杂的财产。
SaveChanges()
在加载复杂属性方面没有做任何事情。最多,如果你要添加新对象,它将设置你的主键属性。
你的专栏reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
加载Campaign
的方式也没有任何作用,因为您的奖励对象没有附加到上下文中。您需要显式地告诉EF加载该复杂对象或附加它然后让延迟加载发挥其魔力。
因此,在您context.SaveChanges();
之后,您有三个加载reward.Campaign
的选项:
Attach()
奖励上下文,以便Campaign
可以延迟加载(在访问时加载)
context.Rewards.Attach(reward);
注意:您只能在上下文范围内延迟加载reward.Campaign
,因此如果您不打算在上下文生命周期内访问任何属性,请使用选项2或3.
手动Load()
Campaign
属性
context.Entry(reward).Reference(c => c.Campaign).Load();
手动Include()
Campaign
属性
reward = context.Rewards.Include("Campaigns")
.SingleOrDefault(r => r.Id == reward.Id);
虽然,我建议Load
,因为你已经在内存中reward
了。
查看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)
如果您具有多个导航属性或要添加多个记录,则可能很难通过这些方式进行。
所以我建议如果内存没关系,请在添加记录后创建一个新的上下文对象,并改用它