38

我将这个实体类与 Entity Framework 5 Code First 一起使用:

public class Survey
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string SurveyName { get; set; }

    [Required]
    public int ClientID { get; set; }

    [ForeignKey("ClientID")]
    public virtual Client Client { get; set; }
}

在我的控制器的 Create 方法中,我这样做:

    Survey entity = new Survey()
    {
        SurveyName = "Test Name",
        ClientID = 4
    };
    db.Surveys.Add(entity);
    db.SaveChanges();
    Client c1 = entity.Client;                    //Why is this null?
    Client c2 = db.Clients.Find(entity.ClientID); //But this isn't?

    string s2 = c2.ClientName;
    string s1 = c1.ClientName;   //null reference thrown here

在 SaveChanges 之后,客户端导航属性保持为空。我希望调用从数据库加载客户端,因为存在外键。为什么不这样做?

编辑 这里的代码来自我的控制器依赖于DbContext. 在我完成这项工作后不久,我重新分解了代码以使用存储库和一个工作单元。Create这一举动的部分原因是当我想使用时感觉使用它是错误的new。然后发生的事情是我遇到了如何确保在使用存储库模式时创建代理的问题。

4

4 回答 4

62

为确保在创建父项后导航属性的延迟加载将起作用,您不能Survey使用new运算符创建,而是通过上下文实例创建它,因为它将实例化一个能够延迟加载相关的动态代理Client. 这就是该DbSet<T>.Create()方法的用途:

Survey entity = db.Surveys.Create();

entity.SurveyName = "Test Name";
entity.ClientID = 4;

db.Surveys.Add(entity);
db.SaveChanges();

Client c1 = entity.Client;
string s1 = c1.ClientName;
// will work now if a Client with ID 4 exists in the DB

只是要强调一下:从数据库加载客户端的不是线entity.ClientID = 4;db.Surveys.Add(entity);或线db.SaveChanges,而是线Client c1 = entity.Client;(延迟加载)。

于 2013-03-27T20:44:19.833 回答
11

就像@NicholasButler 说的那样,调用SaveChanges就像它在锡上所说的那样 - 如果您调试代码,您可以看到这一点:Intellitrace 输出将显示它为您正在持久的插入/更新生成的 SQL,但不会有后续选择.

请记住,除非您急切加载(使用该Include方法),否则在执行检索时不会加载相关实体,因此按道理创建/更新它们也不会。

实体框架(我认为是 4.1 及更高版本)支持延迟加载。这意味着如果启用它,类似代码Client c1 = entity.Client; 应该加载该Client对象。需要明确的是,此操作与SaveChanges调用没有直接关系。

检查是否db.Configuration.LazyLoadingEnabled设置为true. 如果没有,请尝试将其设置为true并查看是否Client c1 = entity.Client;仍然为空。

简而言之,调用SaveChanges不会触发加载,但如果启用了延迟加载,则访问entity.Client应该触发实体的加载(如果尚未加载)。

编辑:

我应该早点考虑到这一点,但是您不会对Survey entity对象进行延迟加载。原因是 EF 通过创建一个派生自您的类但覆盖标记为virtual支持延迟加载的属性来发挥其延迟加载的魔力。它在您执行检索时执行此操作,因此您的entity对象不会按原样延迟加载任何内容。

在您致电后尝试此操作SaveChanges

Survey entity2 = db.Surveys.Find(entity.ID);
Client c1 = entity2.Client;

这应该表现出您所追求的行为。

于 2013-03-27T09:42:50.540 回答
0

您需要将Survey类上的所有属性定义为虚拟以启用延迟加载。

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/vstudio/dd468057(v=vs.100).aspx

于 2013-03-21T16:29:15.123 回答
-1

我希望调用从数据库加载客户端,因为存在外键。为什么不这样做?

它没有这样做是因为你没有要求它这样做。在调用 之后SaveChanges(),EF 没有引用行中的数据,并且不会进行潜在的冗余数据库调用来获取它。

调用db.Clients.Find(...告诉 EF 从数据库中获取行,这就是它返回对象的原因。

于 2013-03-21T16:59:26.727 回答