public class CellValue { public double? Max; public double? Min; public string Value; public string Annotation; }
//以下在数据库中查找是否具有这样的条件的数据,如果无返回null
public T GetOne(Expression<Func<T, bool> predicate) { IQueryable<T> query = this.context.Set<T>(); return query.FirstOrDefault(predicate); }
我在调用时总是返回null值,为什么?
CellValue cell = new CellValue{Max = 0.8, Min = 0.1, Value="test"}; Steel steel = service.GetOne(t=>t.Max == cell.Max && t.Min == cell.Min && t.Value == cell.Value && t.Annotation == cell.Annotation); //Steel具有和CellValue一样的属性,但是返回的steel == null
当我把Steel steel = service.GetOne(t=>t.Max == cell.Max
&& t.Min == cell.Min
&& t.Value == cell.Value
&& t.Annotation == cell.Annotation);
中的cell.Max cell.Min等换成具体的值时,就没有问题,
我想可能我对Expression Tree和Lambda表达式的理解不深刻导致的,
他们在转换时是否做了一些事情,使数值发生了变化,以至于在数据库命中不到记录呢?
http://data.uservoice.com/forums/72025-ado-net-entity-framework-ef-feature-suggestions/suggestions/1015361-incorrect-handling-of-null-variables-in-where-cl?ref=title#suggestion-1015361
用SQL Profiler看看EF实际生成的SQL语句
因为这里是ling to entity,在判断为null时,sql语句写的不对,最后使用了最笨的方法解决:
var conditions = (from t in this.context.Conditions where (val.Annotation == null ? t.Annotation == null : t.Annotation == val.Annotation) && (val.Max.HasValue ? t.Max == val.Max.Value : t.Max == null) && (val.Min.HasValue ? t.Min == val.Min.Value : t.Min == null) && t.ColumnId == 56 && (val.Value == null ? t.Value == null : t.Value == val.Value) select t).ToList();
所以对于有空值的Entity查询似乎要格外小心,看到上面的那个帖子说是在4.5版本后已经解决了,另外一些详细的解答过程可以看这个链接: http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/d00f5611-4fce-4da4-8f79-802336fdfe1a/ 当然看了dudu老大的文章,使用IEnumerable对象查询一个满足条件的实体时,会导致的全部加载实体的问题,最笨的方法是全加载进来,然后在内存中找满足条件的记录。
@Zigzag: 谢谢分享!
@dudu:
Max是double?可空类型。使用:
var conditions3 = (from t in this.context.Conditions where t.Max == val.Max select t).ToList();
用miniprofilerEF输出的语句:
SELECT [Extent1].[ConditionId] AS [ConditionId], [Extent1].[ColumnId] AS [ColumnId], [Extent1].[Min] AS [Min], [Extent1].[Max] AS [Max], [Extent1].[Value] AS [Value], [Extent1].[Annotation] AS [Annotation] FROM [dbo].[Conditions] AS [Extent1] WHERE [Extent1].[Max] = @p__linq__0
使用:
var conditions2 = (from t in this.context.Conditions where t.Max == null select t).ToList();
输出的SQL语句为:
SELECT [Extent1].[ConditionId] AS [ConditionId], [Extent1].[ColumnId] AS [ColumnId], [Extent1].[Min] AS [Min], [Extent1].[Max] AS [Max], [Extent1].[Value] AS [Value], [Extent1].[Annotation] AS [Annotation] FROM [dbo].[Conditions] AS [Extent1] WHERE [Extent1].[Max] IS NULL
使用:
var conditions = (from t in this.context.Conditions where (val.Max.HasValue ? t.Max == val.Max.Value : t.Max == null) select t).ToList();
输出的sql语句为:
SELECT [Extent1].[ConditionId] AS [ConditionId], [Extent1].[ColumnId] AS [ColumnId], [Extent1].[Min] AS [Min], [Extent1].[Max] AS [Max], [Extent1].[Value] AS [Value], [Extent1].[Annotation] AS [Annotation] FROM [dbo].[Conditions] AS [Extent1] WHERE (CASE WHEN (@p__linq__0 IS NOT NULL) THEN CASE WHEN ([Extent1].[Max] = @p__linq__1) THEN cast(1 as bit) WHEN ([Extent1].[Max] <> @p__linq__1) THEN cast(0 as bit) END WHEN ([Extent1].[Max] IS NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1
所以可以看到问题在于,sql语句对于[Extent1].[Max] = NULL是无法解析的。
@Zigzag: 用 where t.Max.HasValue 呢?
@dudu:
SELECT [Extent1].[ConditionId] AS [ConditionId], [Extent1].[ColumnId] AS [ColumnId], [Extent1].[Min] AS [Min], [Extent1].[Max] AS [Max], [Extent1].[Value] AS [Value], [Extent1].[Annotation] AS [Annotation] FROM [dbo].[Conditions] AS [Extent1] WHERE [Extent1].[Max] IS NOT NULL
@Zigzag: 试试下面的代码看能否解决你的问题:
Steel steel = service.GetOne(t=>t.Max.Value == cell.Max.Value && t.Min.Value == cell.Min.Value && t.Value == cell.Value && t.Annotation == cell.Annotation);
@dudu:
SELECT TOP (1) [Extent1].[ConditionId] AS [ConditionId], [Extent1].[ColumnId] AS [ColumnId], [Extent1].[Min] AS [Min], [Extent1].[Max] AS [Max], [Extent1].[Value] AS [Value], [Extent1].[Annotation] AS [Annotation] FROM [dbo].[Conditions] AS [Extent1] WHERE ([Extent1].[Max] = @p__linq__0) AND ([Extent1].[Min] = @p__linq__1) AND ([Extent1].[Value] = @p__linq__2) AND ([Extent1].[Annotation] = @p__linq__3) AND (56 = [Extent1].[ColumnId])
依然无法命中记录。EF少加了IS NULL判断,通过= NULL判断是不对的。
@Zigzag: 是否解决了你的问题?
@Zigzag: 你希望的SQL语句是 [Extent1].[Max] IS NULL?
@dudu: 问题出在cell上,如果你直接用一个值比如是0.38,那么对应的sql 可能就是[Extent1].[Max] = 0.38,如果你直接用null,那么对应的sql语句就是[Extent1].[Max] IS NULL,这都没有问题。但目前你有一个可空的值是Max,不知道是null还是有值,直接用t.Max == cell.Max则出现语句是[Extent1].[Max] = @p__linq__0,如果此时cell.Max拥有一个值,很好,工作正常,得到我们期望的结果。但是如果cell.Max刚好是null,那么就出现了[Extent1].[Max] = null 的语句,显然,这句话在SQL里不会命中任何记录的,在这种情况下我们期望的是 [Extent1].[Max] IS NULL,可是EF很笨,不知道这样去生成SQL语句。
然后,就使用了这样一种很笨的方法, (val.Max.HasValue ? t.Max == val.Max.Value : t.Max == null),你可以看到生成的SQL语句比较复杂,它进行了判断,是我们期望的那样。
很严重的是,即使你的这个Max是string类型,也可能出现这个错误。如果没出现错误,可能的原因是:1.你的cell.Max没有出现的null的情况,2.你直接使用了确定的值,而不是变量。
@Zigzag: 我觉得目前的EF做不到这么智能,只能用这种笨方法。
@dudu: 据说EF4.5修正了这个问题,我还没尝试过。现在都5.0,啥时候才能出现一个稳定的版本。选择EF的原因就是开发快,实际上在使用过程中遇到的小问题也很多,也费了不少时间。
@Zigzag: 我们也是,有些复杂查询不得不改用存储过程
public class CellValue { double? Max; double? Min; string Value; string Annotation; }
这里属性应该是公有的吧,不然外面没法访问啊
问题可能的原因是Annotation的值为null,所以在查询时没有结果,你把Annotation也初始化一下试试
1. CellValue属性肯定是public的,我这里忽略了,不然怎么访问属性啊。
2. 问题出在double? max和double? Min上,这两个是null的话,简单的使用cell.Max == t.Max是不正确的,这个貌似是个bug,http://stackoverflow.com/questions/682429/how-can-i-query-for-null-values-in-entity-framework,有说明,我换了这个帖子的几种方法,都不实用。
3.路过的人如果出现这种使用nullable的值做表达式时特别要注意了。
4.目前依然没有找到解决方法,为啥用Object.Equals()也不能解决呢。这个是表达式树的问题?
题外话:你的图像和园子里的大牛Artech的可真像,我以为是Artech改名了呢。。。。:-)
@Zigzag: 汗
其实你应该在条件中使用
cell.Max.Value, cell.Min.Value
感谢提供思路,我仔细检查了下,的确是Max和Min引起的错误,但是直接这么写也是不对的,因为可能为null。
@Zigzag:
试试这个吧
service.GetOne(t=>(cell.Max.HasValue ? t.Max == cell.Max.Value : true)
&&(cell.Min.HasValue ? t.Min == cell.Min.Value : true)
&& t.Value == cell.Value
&& t.Annotation == cell.Annotation);
1、CellValue的成员必须是PUBLIC,EF不支持私有成员
2、先不要做成服务,简单的EF下看是否有结果。