我有以下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时是不允许的?我的查询怎么样导致这个东西不起作用?
答案 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");
答案 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