【问题标题】:Entity Framework Include() is not working实体框架 Include() 不起作用
【发布时间】:2010-12-17 21:37:35
【问题描述】:

我有以下 EF 查询:

TestEntities db = new TestEntities();
var questions = from q in db.Questions.Include("QuestionType")
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

foreach( var question in questions ) {
    // ERROR: Null Reference Exception
    Console.WriteLine("Question Type: " + question.QuestionType.Description);
}

当我访问 QuestionType 属性时,我得到一个空引用异常。我正在使用 Include("QuestionType") 但它似乎不起作用。我做错了什么?

编辑:当我打开延迟加载时,它不会抛出空引用异常。

编辑:当我执行以下操作时,Include() 似乎正在工作:

var questions = db.Questions.Include("QuestionType").Select(q => q);

当我对一个单独的实体进行断言时,Include 似乎失败了。使用包含时不允许这样做吗?我的查询导致这件事不起作用怎么办?

【问题讨论】:

  • 数据库或模型中的 Question 和 QuestionType 之间是否存在已定义的关系?
  • 是的。启用 LazyLoading 时,它将加载对象。
  • 您应该使用 lambdas 而不是“魔术字符串” - 您需要添加“使用 System.Data.Entity” - 请参阅 stackoverflow.com/a/14518244/16940
  • 检查、仔细检查、三重检查您的配置是否设置正确!被这个烧死了,我的关系指向了错误的外键

标签: c# entity-framework entity-framework-4


【解决方案1】:

问题可能与您的 Linq 表达式中的子查询有关。子选择、分组和投影可能会导致使用 Include 的急切加载以静默方式失败,如 here 所述并更详细地解释 here(请参阅线程中间某处的 Diego Vega 的答案)。

虽然我看不出您在使用这些帖子中描述的Include 时违反了任何规则,但您可以尝试根据建议更改查询:

var questions = from q in db.Questions
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

var questionsWithInclude = ((ObjectQuery)questions).Include("QuestionType");

foreach( var question in questionsWithInclude ) {
    Console.WriteLine("Question Type: " + question.QuestionType.Description);
}

(或者使用帖子中提到的扩展方法。)

如果我正确理解链接的帖子,这并不一定意味着它现在可以工作(可能不会),但您会收到一个异常,为您提供有关问题的更多详细信息。

【讨论】:

  • 这行得通。我不得不将它转换为 ObjectQuery 但之后它就起作用了。
  • 很好,作为补充:var questions = (from sq in db.SurveyQuestions where sq.Survey == surveyTypeID orderby sq.Order select q.Question).Include(“QuestionType”); 可能更干净一点。
  • hmmm 我正在使用这种方法,但它在第一次命中时仍然不起作用。如果我在并行后续调用中执行相同的查询,但不是第一个。还在想办法。
  • 这在 Entity Framework Core(至少 v2)中不起作用。我无法转换为 (ObjectQuery 或 ObjectQuery)
  • 我爱你。您提供的这篇文章描述了我在结合 Select 和 Include 时遇到的两天问题。
【解决方案2】:

添加“System.Data.Entity”,您将能够在 IQueryable 上调用 Include:

var questions = from q in db.Questions
                from sq in db.SurveyQuestions
                where sq.Survey == surveyTypeID
                orderby sq.Order
                select q;

questions = questions.Include("QuestionType");

见:How can i convert a DBQuery<T> to an ObjectQuery<T>?

【讨论】:

  • 为了更好地实现这一点,您可以使用该命名空间中的 Include 扩展方法来完全摆脱字符串文字:questions.Include(q => q.QuestionType);
  • 谢谢 - 遇到了一个非常奇怪的问题并意识到我们没有将 System.Data.Entity 命名空间添加到我们的类中!!
【解决方案3】:

我遇到了Include(e =&gt; e.NavigationProperty) 不工作的问题,但解决方案与上述不同。

有问题的代码如下:

    UserTopic existingUserTopic = _context.UserTopics
            .Include(ut => ut.Topic)
            .FirstOrDefault(t => t.UserId == currentUserId && t.TopicId == topicId);

        if (existingUserTopic != null)
        {
            var entry = _context.Entry(existingUserTopic);
            entry.State = EntityState.Deleted;

            if (existingUserTopic.Topic.UserCreated) 
            {
                var topicEntry = _context.Entry(existingUserTopic.Topic);
                entry.State = EntityState.Deleted;
            }

            await _context.SaveChangesAsync();
        }

所以问题在于代码的顺序。一旦实体被标记为EntityState.Deleted,实体框架似乎就会使内存中的导航属性无效。所以要在我的代码中访问existingUserTopic.Topic,我必须在将existingUserTopic标记为已删除之前这样做。

【讨论】:

  • 我现在正面临这个确切的问题。但是,我不能只是重新排列代码的顺序:/ 你知道是否还有其他方法可以让 include 起作用吗?
【解决方案4】:

由于这个问题是“Entity Framework Include not working”的热门搜索结果,我只想提几个其他可能性,尽管它们都与 @Dismissile 的原始帖子无关。

区分大小写

SQL Server(可能还有其他数据库平台)通常以不区分大小写的方式工作。因此,如果您有一个主键值 ABC1,数据库将接受 ABC1、abc1、AbC1 等作为有效的外键值。但是,.Net 字符串比较默认区分大小写,因此即使您的 .Include 生成额外的 SQL 以将额外的值拉入 EF,如果键中存在大小写差异,它也可能无法填充子对象。这在SO question 中有更深入的讨论,并附有几个很好的链接。对主键和外键列使用区分大小写的排序规则可以降低导致 .Include 失败的风险。

尾随空格

这让我浪费了一天的时间试图找出我的 .Include 不起作用的原因。 SQL Server(可能还有其他数据库平台)通常会忽略字符串比较中的尾随空格。因此,如果您有一个主键值(不包括引号)“ABC”(一个尾随空格),数据库将接受“ABC”(一个空格)、“ABC”(无空格)、“ABC”(2 个空格) ) 等作为有效的外键值。但是,.Net 字符串比较不会忽略尾随空格,因此即使您的 .Include 生成额外的 SQL 以将额外的值拉入 EF,如果键中的尾随空格存在差异,它也可能无法填充子对象。此MS Support 页面中描述了SQL Server 行为。我还没有制定出防止这种情况的好策略。除了仔细的数据管理之外,还包括失败,即不要让用户输入外键值 - 使用下拉列表,或者虔诚地 rtrim 用户输入。

【讨论】:

    【解决方案5】:

    以下是在所有类型的查询中执行此操作的方法。您不需要使用“包含”。唯一的问题是,这似乎不适用于多对多导航属性。

    只需将您想要的导航属性作为“虚拟”属性添加到最终结果中。

    (这适用于更改跟踪代理。我没有在其他情况下测试过。另外,不要指定“.AsNoTracking()”)

       var results = context.Categories.Where(...)
          .GroupJoin(
             context.Books.Where(...),
             cat => cat.Id,
             book => book.CategoryId, 
             (cat, books) => new 
             {
                 Category = cat,
                 Books = books.ToList()
                 Dummy_Authors = books.Select(b => b.Author).ToList() // dummy property
             });
    

    现在,如果你这样做,数据库将不会被再次查询。

    var cat1 = results.First(); // query executed here
    var authorName = cat1.Books.First().Author.Name; // already loaded
    

    【讨论】:

    • 我建议不要使用这种方法。除了代码结构笨拙之外,还容易出现可读性问题; EF 生成的查询也很笨拙和繁重。而使用Include(),则提供了干净的连接以及语义和意图的自然流动。
    • 毛里西奥,我不认为你理解这个问题。我这样做是因为在某些情况下 Include() 不起作用。 +5
    • 通过创造另一个问题来解决一个问题并不是一个好的解决方案。这就是代码库最终变得无法为新开发人员维护的原因。
    猜你喜欢
    • 2022-01-24
    • 2011-04-28
    • 2017-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 2013-04-19
    相关资源
    最近更新 更多