【问题标题】:Entity Framework Lazy Loading Does Not Work实体框架延迟加载不起作用
【发布时间】:2014-08-17 22:37:46
【问题描述】:

我正在将我的项目从 EF 4.0 迁移到 EF 6.0。 插入新实体后,我试图从导航实体中获取属性。该代码在 EF 4.0 中运行没有任何问题,但是当我尝试在 EF 6.0 中运行时,我遇到了 NullReference 异常。

用户实体:

public partial class users
{
    public int RID { get; set; }
    public string Name { get; set; }
    public Nullable<int> LangRef { get; set; }

    public virtual language language { get; set; }
}

语言实体:

public partial class language
{
    public language()
    {
        this.users = new HashSet<users>();
    }

    public int RID { get; set; }
    public string Name { get; set; }

    public virtual ICollection<users> users { get; set; }
}

测试代码:

test1Entities testEnt = new test1Entities();
users user = new users();
user.Name = "asd";
user.LangRef = 1;//That ID refer to English record in Language entity
testEnt.users.Add(user);
testEnt.SaveChanges();

string lang = user.language.Name;//Should return "English". However language property seem null

型号:

【问题讨论】:

  • 你在哪里为新用户设置语言属性?您可以设置显式外键,例如 languageId 或将语言属性设置为 DbContext 正在跟踪的语言实体。
  • 您查看过msdn 上的文档吗?在我看来,语言和 LangRef 属性没有被正确映射。
  • @kjbartel 我正在使用数据库优先方法。我不对自动生成的代码进行任何编辑。该模型应该可以正常工作。我找不到哪里出错了。
  • 我会尝试重新生成模型。不确定您是否自定义了任何模板,但作为测试,您可以创建一个新项目并从头开始创建一个新模型,看看它是否有效。我想模板在 4 到 6 之间发生了一些变化,所以你可能不能按原样使用旧的。

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


【解决方案1】:

你说user.language.Name 应该返回英文,你没有在任何地方设置它。您必须在创建新用户时设置语言。

test1Entities testEnt = new test1Entities();
users user = new users();
user.Name = "asd";
user.LangRef = 1;

// grab the english language entity from the database
language englishLanguage = entities.First(o => o.Name == "English");

// set the users language to english
user.language = englishLanuage;

testEnt.users.Add(user);
testEnt.SaveChanges();

想象一下,如果您使用原始 SQL 插入用户,您会运行类似这样的操作

INSERT INTO users (name, languageId) VALUES ('Foo', 1);

其中 1 是语言表中映射到英语的记录的 RID。 1 是用户表中的外键,它将用户映射到他们的语言。与原始 SQL 相同,您必须明确告诉 Entity Framework 用户使用哪种语言。这是通过在 DbContext 中查询语言(返回跟踪的实体),然后将该实体设置为导航属性来完成的。


或者,如果您事先知道语言记录的主键,您可以创建一个新的语言实体(具有正确的 RID)并将其附加到 DbContext,然后将其设置为导航属性。这样可以减少对数据库的查询。虽然这会更有效,但你应该做任何对你来说更容易/最有意义的事情。让它工作,然后您可以稍后优化瓶颈。

language englishLanguage = new language() { RID = 1, Name = "English" };
testEnt.languages.Attach(language);

user.language = englishLanguage;
testEnt.users.Add(user);

编辑

鉴于您最近的编辑,现在您想要做什么更清楚了。我没有意识到 LangRef 是您的外键,因为它是小数并且不遵循正常的命名约定。

此答案https://stackoverflow.com/a/18251442/1160036 中描述了解决方案。问题来自实体框架缓存。在SaveChanges()之后(这样RID就更新了),可以拆下重新拉用户刷新一下。

testEnt.users.Add(user);
testEnt.SaveChanges();
((IObjectContextAdapter)testEnt).ObjectContext.Detach(user);
users user = testEnt.First(o => o.RID == user.RID);
Console.WriteLine(user.language.Name);

打开一个新的 DbContext 也可以,但这确实会导致与数据库建立另一个连接。

如上所述,testEnt.Entry&lt;users&gt;(user).Reload() 很遗憾不起作用。

【讨论】:

  • 如果您查看模型图像,它们已经相互导航。如果我执行该查询,everythink 工作正常: users newUser = testEnt.users.Include("language").FirstOrDefault(x => x.RID == user.RID);但是,这称为急切加载。当我尝试访问 Language.Name 属性时的正常情况 EF 应该对语言进行新的查询。我想我错过了一些配置细节。
  • @Dreamcatcher 我能够复制并解决您的问题。我已经编辑了我的答案。
  • 谢谢你的回答也解决了问题,.Include("language") 和 DatabaseObjectContext.Entry(member).Reload(); 解决了.但是,我在将项目从 Entity Framework 4.0 迁移到 Entity Framework 6.0 之前提到过。并且有成百上千行代码。如果我要开始编辑代码,它会消耗大量时间。我认为延迟加载有助于喜欢这种情况。有什么方法可以禁用 EF 缓存吗?谢谢...
  • @Dreamcatcher 不,奇怪的是,除了已经讨论过的方式(急切加载、新 DbContext 或分离)之外,似乎没有禁用缓存的全局方式。我最好的建议是尝试抽象出数据库层,这样当您需要进行更改时,您只需在一个集中的位置进行更改,而不是在整个应用程序中进行。
【解决方案2】:

不要使用new users() 试试testEnt.users.Create()。不同之处在于返回的对象将是延迟加载导航属性所需的代理类。

作为助手,当您调用SaveChanges() 时,执行相反的操作并设置language 导航属性本身而不是LangRef 外键值实际上应该正确设置LangRef 属性。

请参阅 MSDN 上的 DbSet.Create method。

【讨论】:

  • 谢谢。我认为这对我的情况来说是更有效的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-11
  • 1970-01-01
相关资源
最近更新 更多