实体框架Include()不起作用

时间:2010-12-17 21:37:35

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

我有以下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似乎失败了。使用Include时是不允许的?我的查询怎么样导致这个东西不起作用?

5 个答案:

答案 0 :(得分:55)

问题可能与Linq表达式中的子查询有关。子选择,分组和投影可能导致急切加载Include无声地失败,如上所述here并更详细地解释here(参见线程中间某处迭戈维加的答案)

虽然我无法确定您在使用这些帖子中描述的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);
}

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

如果我正确理解链接的帖子,这并不一定意味着它现在可以正常工作(可能不会),但是你会得到一个例外,为你提供有关问题的更多细节。

答案 1 :(得分:24)

添加“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>?

答案 2 :(得分:4)

我遇到了Include(e => 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之前执行此操作。

答案 3 :(得分:0)

由于此问题是“实体框架包含不起作用”的首要搜索结果,我只想提一下其他几种可能性,即使它们都不与@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的行为。除了精心的数据管理外,我还没有制定出防止此类错误的好策略。包括失败,即不要让用户键入外键值-使用下拉列表,或认真地限制用户输入。

答案 4 :(得分:-2)

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

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

(这适用于更改跟踪代理。我没有在其他情况下测试它。另外,不要指定“.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