这是我在实体框架中执行的简单linq
查询
db.Responses.FirstOrDefault(r => r.QuestionId.Equals(“1.1.1”) && r.User.Id.Equals(user.Id) && !r.IsDeleted);
此处QuestionId
是varchar
数据类型列,db是上下文对象。
我启动了实体分析器,看看底层发生了什么,底层SQL查询似乎有一堆看似有点多余的陈述
/* 1 */ SELECT TOP (1) *
/* 2 */ FROM [dbo].[Responses] AS [Extent1]
/* 3 */ WHERE ((([Extent1].[QuestionId] = '1.1.1' /* @p__linq__0 */)
/* 4 */ AND (NOT ([Extent1].[QuestionId] IS NULL
/* 5 */ OR '1.1.1' /* @p__linq__0 */ IS NULL)))
/* 6 */ OR (([Extent1].[QuestionId] IS NULL)
/* 7 */ AND ('1.1.1' /* @p__linq__0 */ IS NULL)))
/* 8 */ AND ([Extent1].[UserId] = 1 /* @p__linq__1 */)
/* 9 */ AND (1 /* @p__linq__1 */ IS NOT NULL)
/* 10 */ AND ([Extent1].[IsDeleted] <> cast(1 as bit))
请注意第4-7行中的所有额外代码,将QuestionId
更改为null
不会将查询简化为
/* 1 */ SELECT TOP (1) *
/* 2 */ FROM [dbo].[Responses] AS [Extent1]
/* 3 */ WHERE ([Extent1].[QuestionId] = '1.1.1' /* @p__linq__0 */)
/* 4 */ AND ('1.1.1' /* @p__linq__0 */ IS NOT NULL)
/* 5 */ AND ([Extent1].[UserId] = 1 /* @p__linq__1 */)
/* 6 */ AND (1 /* @p__linq__1 */ IS NOT NULL)
/* 7 */ AND ([Extent1].[IsDeleted] <> cast(1 as bit))
所以,问题是为什么实体框架会放入所有额外的代码? 为什么第4行和第4行第6行必要,where子句中唯一相关的陈述是第3,5和7行,
我正在尝试优化我的SQL语句,并且任何关于为什么实体框架以这种方式执行操作的想法都会有所帮助。我在Visual Studio 2013中使用EF6。
答案 0 :(得分:3)
行:
/* 3 */ WHERE ((([Extent1].[QuestionId] = '1.1.1' /* @p__linq__0 */)
/* 4 */ AND (NOT ([Extent1].[QuestionId] IS NULL
/* 5 */ OR '1.1.1' /* @p__linq__0 */ IS NULL)))
应该解释C#/ VB.NET和SQL之间空值比较中语义的差异。您可以使用DbContext.Configuration.UseDatabaseNullSemantics
或ObjectContextOptions.UseCSharpNullComparisonBehavior
来控制行为。您可以找到更多详细信息here和here。
答案 1 :(得分:2)
这是因为字符串'1.1.1'作为sql命令参数传递,而SQL生成器对参数值一无所知。 Ef不会根据值生成不同的语句。即使传递的值为null,语句也必须正确。
如果Column可以为空,则列和参数值都为null时,等于必须为true。当它不可为空时,只有当传递的值不为空时才会成立。
EF所做的每件事都是100%有效的,而且它的实现非常正确。除非你得到错误的结果,否则不要试图优化。