首页新闻找找看学习计划

Entity Framework中一个表达式的问题

0
悬赏园豆:10 [已解决问题] 解决于 2012-07-17 09:12
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

Zigzag的主页 Zigzag | 初学一级 | 园豆:70
提问于:2012-06-06 21:34
< >
分享
最佳答案
1

用SQL Profiler看看EF实际生成的SQL语句

收获园豆:10
dudu | 高人七级 |园豆:38642 | 2012-06-07 10:35

因为这里是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 | 园豆:70 (初学一级) | 2012-06-07 11:41

@Zigzag: 谢谢分享!

dudu | 园豆:38642 (高人七级) | 2012-06-07 12:07

@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 | 园豆:70 (初学一级) | 2012-06-07 12:18

@Zigzag: 用 where t.Max.HasValue 呢?

dudu | 园豆:38642 (高人七级) | 2012-06-07 13:04

@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 | 园豆:70 (初学一级) | 2012-06-07 13:18

@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 | 园豆:38642 (高人七级) | 2012-06-07 13:37

@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 | 园豆:70 (初学一级) | 2012-06-07 15:13

@Zigzag: 是否解决了你的问题?

dudu | 园豆:38642 (高人七级) | 2012-06-07 15:14

@Zigzag: 你希望的SQL语句是 [Extent1].[Max] IS NULL?

dudu | 园豆:38642 (高人七级) | 2012-06-07 15:19

@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 | 园豆:70 (初学一级) | 2012-06-07 15:27

@Zigzag: 我觉得目前的EF做不到这么智能,只能用这种笨方法。

dudu | 园豆:38642 (高人七级) | 2012-06-07 15:32

@dudu: 据说EF4.5修正了这个问题,我还没尝试过。现在都5.0,啥时候才能出现一个稳定的版本。选择EF的原因就是开发快,实际上在使用过程中遇到的小问题也很多,也费了不少时间。

Zigzag | 园豆:70 (初学一级) | 2012-06-07 15:35

@Zigzag: 我们也是,有些复杂查询不得不改用存储过程

dudu | 园豆:38642 (高人七级) | 2012-06-07 15:40
其他回答(3)
0
public class CellValue
{
    double? Max;
    double? Min;
    string  Value;
    string Annotation;
}

这里属性应该是公有的吧,不然外面没法访问啊

问题可能的原因是Annotation的值为null,所以在查询时没有结果,你把Annotation也初始化一下试试

artwl | 园豆:16526 (专家六级) | 2012-06-06 22:26

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()也不能解决呢。这个是表达式树的问题?

支持(0) 反对(0) Zigzag | 园豆:70 (初学一级) | 2012-06-07 09:17

题外话:你的图像和园子里的大牛Artech的可真像,我以为是Artech改名了呢。。。。:-)

支持(0) 反对(0) Zigzag | 园豆:70 (初学一级) | 2012-06-07 09:23

@Zigzag: 汗

支持(0) 反对(0) artwl | 园豆:16526 (专家六级) | 2012-06-07 09:26
0

其实你应该在条件中使用 

cell.Max.Value,  cell.Min.Value
gunsmoke | 园豆:3592 (老鸟四级) | 2012-06-07 07:33

感谢提供思路,我仔细检查了下,的确是Max和Min引起的错误,但是直接这么写也是不对的,因为可能为null。

支持(0) 反对(0) Zigzag | 园豆:70 (初学一级) | 2012-06-07 09:18

@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);
支持(0) 反对(0) gunsmoke | 园豆:3592 (老鸟四级) | 2012-06-08 11:43
0

1、CellValue的成员必须是PUBLIC,EF不支持私有成员

2、先不要做成服务,简单的EF下看是否有结果。

无之无 | 园豆:5085 (大侠五级) | 2012-06-07 08:22
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册