我如何查询entity framework中的空值?
我想执行这样的查询
var result = from entry in table where entry.something == null select entry;
并获得生成的IS NULL
。
编辑:在前两个答案后,我觉得有必要澄清,我正在使用entity framework,而不是LINQ到SQL。 object.Equals()方法在EF中似乎不起作用。
编辑2:上述查询按预期工作。 它正确地生成IS NULL
。 我的生产代码是
value = null; var result = from entry in table where entry.something == value select entry;
生成的SQL是something = @p; @p = NULL
something = @p; @p = NULL
。 EF似乎正确地翻译了常量expression式,但是如果涉及一个variables,它就像正常的比较那样对待它。 实际上是有道理的。 我会解决这个问题
Linq-to-SQL的解决方法:
var result = from entry in table where entry.something.Equals(value) select entry;
Linq-to-Entities(ouch!)的解决方法:
var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry;
这是一个令我感到厌恶的恶臭。 如果这个bug也影响到你,请访问UserVoice上的bug报告 ,让微软知道这个bug也影响到你。
编辑: 这个错误正在EF 4.5中修复 ! 感谢大家upvoting这个错误!
为了向后兼容,它将被选中 – 你需要手动启用一个设置来使entry == value
工作。 没有关于这个设置是什么的话。 敬请关注!
编辑2:根据EF小组的这个post , 这个问题已经在EF6中修复了! 呜呼!
我们改变了EF6的默认行为来补偿三值逻辑。
这意味着依赖旧行为( null != null
,但仅在与variables进行比较时)的现有代码将需要更改为不依赖于该行为,或将UseCSharpNullComparisonBehavior
设置为false以使用旧的中断行为。
有一个稍微简单的解决方法,适用于LINQ to Entities:
var result = from entry in table where entry.something == value || (value == null && entry.something == null) select entry;
正如AZ注意到的,这是因为LINQ to Entities特殊情况x == null(即与null常量相等的比较),并将其转换为x IS NULL。
目前我们正在考虑改变这种行为,如果平等的双方都是可以空的,就自动引入补偿比较。 但是有一些挑战:
- 这可能会破坏已经取决于现有行为的代码。
- 即使在很less使用空参数的情况下,新的翻译也会影响现有查询的性能。
无论如何,我们是否能够在这方面开展工作将取决于我们的客户给予的相对重点。 如果您关心这个问题,我鼓励您在我们的新functionbuild议网站https://data.uservoice.com上投票。;
从Entity Framework 5.0开始,您可以使用以下代码来解决您的问题:
public abstract class YourContext : DbContext { public YourContext() { (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; } }
这应该可以解决你的问题,因为Entity Framerwork将使用“C#like”null比较。
如果它是可以为空的types,也许尝试使用HasValue属性?
var result = from entry in table where !entry.something.HasValue select entry;
没有任何EF在这里testing,虽然…只是一个build议=)
var result = from entry in table where entry.something.Equals(null) select entry;
MSDN参考 : LINQ to SQL:关系数据的.NET语言集成查询
处理空比较使用Object.Equals()
而不是==
检查这个参考
var result = from entry in table where entry.something == null select entry;
上述查询按预期工作。 它正确地生成IS NULL。 我的生产代码是
var value = null; var result = from entry in table where entry.something == value select entry;
生成的SQL是@p; @p = NULL。 EF似乎正确地翻译了常量expression式,但是如果涉及一个variables,它就像正常的比较那样对待它。 实际上是有道理的。
指出所有的entity framework<6.0build议会产生一些尴尬的SQL。 查看第二个“干净”修复的例子。
荒谬的解决方法
// comparing against this... Foo item = ... return DataModel.Foos.FirstOrDefault(o => o.ProductID == item.ProductID // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null && o.Width == w && o.Height == h );
导致SQL如下所示:
SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE (CASE WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit) WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit) END WHEN (([Extent1].[ProductStyleID] IS NULL) AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit) WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit) END WHEN (([Extent1].[MountingID] IS NULL) AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit) WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit) END WHEN (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit) WHEN (NOT (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit) END) = 1
离谱的解决方法
如果你想生成更干净的SQL,如下所示:
// outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame; if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID; else filterProductStyle = o => o.ProductStyleID == null; if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID; else filterMounting = o => o.MountingID == null; if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID; else filterFrame = o => o.FrameID == null; return DataModel.Foos.Where(o => o.ProductID == item.ProductID && o.Width == w && o.Height == h ) // continue the outrageous workaround for proper sql .Where(filterProductStyle) .Where(filterMounting) .Where(filterFrame) .FirstOrDefault() ;
结果在你想要的东西:
SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE ([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND ([Extent1].[Width] = 16 /* @p__linq__1 */) AND ([Extent1].[Height] = 20 /* @p__linq__2 */) AND ([Extent1].[ProductStyleID] IS NULL) AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */) AND ([Extent1].[FrameID] IS NULL)
看来Linq2Sql也有这个“问题”。 看来,由于ANSI NULL是ON还是OFF,这种行为有一个合理的原因,但是令人惊讶的是,为什么直接的“== null”实际上可以像你期望的那样工作。
就个人而言,我更喜欢:
var result = from entry in table where (entry.something??0)==(value??0) select entry;
过度
var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry;
因为它可以防止重复 – 尽pipe这在math上不是确切的,但它适合大多数情况。
我不能评论divega的文章,但是在这里介绍的不同解决scheme中,divega的解决scheme产生了最好的SQL。 性能明智和长度明智。 我刚刚检查了SQL Server Profiler并查看了执行计划(“SET STATISTICS PROFILE ON”)。
不幸的是在Entity Framework 5 DbContext中这个问题仍然没有解决。
我使用了这种解决方法(适用于MSSQL 2012,但ANSI NULLS设置可能会在以后的MSSQL版本中被弃用)。
public class Context : DbContext { public Context() : base("name=Context") { this.Database.Connection.StateChange += Connection_StateChange; } void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e) { // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework // that is not fixed in EF 5 when using DbContext. if (e.CurrentState == System.Data.ConnectionState.Open) { var connection = (System.Data.Common.DbConnection)sender; using (var cmd = connection.CreateCommand()) { cmd.CommandText = "SET ANSI_NULLS OFF"; cmd.ExecuteNonQuery(); } } } }
应该指出,这是一个肮脏的解决方法,但它是一个可以很快实施,适用于所有查询。
如果你喜欢使用方法(lambda)语法,你可以做同样的事情:
var result = new TableName(); using(var db = new EFObjectContext) { var query = db.TableName; query = value1 == null ? query.Where(tbl => tbl.entry1 == null) : query.Where(tbl => tbl.entry1 == value1); query = value2 == null ? query.Where(tbl => tbl.entry2 == null) : query.Where(tbl => tbl.entry2 == value2); result = query .Select(tbl => tbl) .FirstOrDefault(); // Inspect the value of the trace variable below to see the sql generated by EF var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString(); } return result;
var result = from entry in table where entry.something == value||entry.something == null select entry;
使用它