实体框架:我设置外键,SaveChanges然后访问导航属性,但它不加载相关实体.为什么不?

Col*_*lin 36 lazy-loading ef-code-first entity-framework-5

我将这个Entity类与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; }
}
Run Code Online (Sandbox Code Playgroud)

在我的Controller的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
Run Code Online (Sandbox Code Playgroud)

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

编辑 这里的代码来自我的控制器依赖DbContext.在我开始工作后不久,我重新考虑了使用存储库和工作单元的代码.这一举动的一部分是由于Create我想在使用时感觉不对的事实new.然后发生的事情是我遇到了如何确保在使用存储库模式时创建代理的问题.

Sla*_*uma 57

为了确保导航属性的懒加载将工作你已经创建父后,你不能创建Surveynew运营商,但创造它通过上下文实例的手段,因为它会实例化一个动态代理,它能够懒洋洋地加载相关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
Run Code Online (Sandbox Code Playgroud)

只是强调:这不是线entity.ClientID = 4;db.Surveys.Add(entity);db.SaveChanges从数据库加载客户端,但线路Client c1 = entity.Client;(延迟加载).


nic*_*k_w 10

就像@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;
Run Code Online (Sandbox Code Playgroud)

这应该表现出你所追求的行为.

  • 顿悟!"原因是EF通过创建一个派生自你的类,但重写标记为虚拟的属性以支持延迟加载来运行其延迟加载魔法".谢谢你.关于这个建议的一件事是SaveChanges之后的Find会导致额外的数据库命中.我f (2认同)