实体框架 - 荒谬的查询,将smallint转换为int进行比较

时间:2014-03-03 14:35:17

标签: entity-framework entity-framework-6

这里的想法。我有一个简单的表,模型首先与Entity Framework映射,我得到以下SQL生成:

(@p__linq__0 int,@p__linq__1 int)SELECT 
    [Extent1].[BucketRef] AS [BucketRef], 
    [Extent1].[VariantNo] AS [VariantNo], 
    [Extent1].[SliceNo] AS [SliceNo], 
    [Extent1].[TradeNo] AS [TradeNo], 
    [Extent1].[TradeBegin] AS [TradeBegin], 
    [Extent1].[TradeEnd] AS [TradeEnd], 
    FROM [simstg].[Trade] AS [Extent1]
    WHERE ((( CAST( [Extent1].[BucketRef] AS int) = @p__linq__0) AND ( NOT (( CAST( [Extent1].[BucketRef] AS int) IS NULL) OR (@p__linq__0 IS NULL)))) OR (( CAST( [Extent1].[BucketRef] AS int) IS NULL) AND (@p__linq__0 IS NULL))) AND ((( CAST( [Extent1].[VariantNo] AS int) = @p__linq__1) AND ( NOT (( CAST( [Extent1].[VariantNo] AS int) IS NULL) OR (@p__linq__1 IS NULL)))) OR (( CAST( [Extent1].[VariantNo] AS int) IS NULL) AND (@p__linq__1 IS NULL)))

所有演员都会杀死他们。我很遗憾没有看到他们来自哪里。

有问题的查询是:

var tradesQuery = repository.SimStgTrade
    .Where(x => x.BucketRef == bucketId && x.VariantNo == set)
    .ToArray();

这很简单。字段定义为:bucketId:short(数据库中为smallint),设置为short,smallint为数据库。因此,完全不需要演员表。我已经删除并重新创建了模型中的表 - 据我所知,映射匹配(字段为smallint)。因此,我们遇到了严重的性能问题 - 例如:查询超时,因为它不使用表扫描。

任何人都知道如何摆脱那些演员并迫使比较基于短裤?从SQL中可以看出,EF决定首先将所有内容移动到一个int ......这没有任何意义。

这不是“很好”的事情。优秀的查询路径完全不同,结果代码将其转换为自连接。在服务器管理器中,EF变体需要超过5分钟,而带有简单SQL的优化版本需要0.0秒(在该表中返回数十亿的228行)。

2 个答案:

答案 0 :(得分:16)

这种行为对于不同的LINQ提供程序来说很常见,而且不仅仅是EF特定的,因为C#编译器如何为Where表达式生成表达式树。

将条件指定为:

.Where(x => x.BucketRef == bucketId)

并且BucketRef和bucketId都是short,编译器为两个比较部分生成从short到int的转换,因为== operator没有为Short类型定义。这在答案https://stackoverflow.com/a/18584429/869184

中有解释

作为一种解决方法,您可以通过以下方式重写条件:

.Where(x => x.BucketRef.Equals(bucketId))

这有效地从比较中删除了演员。

答案 1 :(得分:2)

您需要使用Expression类中的静态函数自己创建Where表达式。

像这样:

Int16 bucketId = 3;

var parameter = Expression.Parameter(typeof(SimStgTrade));
var property = Expression.PropertyOrField(parameter, "BucketRef");
var constant = Expression.Constant(bucketId);
var comparison = Expression.Equal(property, constant);

var lambda = Expression.Lambda<Func<SimStgTrade,bool>>(comparison, parameter);

var tradesQuery = repository.SimStgTrade
  .Where(lambda)
  .Where(x => x.VariantNo == set)
  .ToArray();

VariantNo == set执行同样的操作以删除该演员表