tinyint列的生成查询将CAST引入int

我正在查询tinyint列,entity framework生成一个SELECT查询,该查询为此列引入了CAST到INT,即使我在WHERE子句中使用的值是字节类型。

查看模型,我的tinyint列生成的Type是byte。

看代码:

byte byteValue = 6; var entityList = from r in rep.DataContext.FooTable where r.TinyintColumn == byteValue select r; 

查看生成的查询:

 SELECT [Extent1].[TinyintColumn] AS [TinyintColumn] WHERE @p__linq__0 = CAST( [Extent1].[TinyintColumn] AS int) 

我对性能有严格的限制,所以我不希望任何选择中的那些CAST。

所以我的问题是,有没有办法避免这个CAST超过列tinyint? 或者我做错了什么?

提前致谢。

如果使用IList.Contains with List则entity framework将不会IList.Contains

 List byteValue = new List { 6 }; var entityList = from r in rep.DataContext.FooTable where byteValue.Contains(r.TinyintColumn) select r; 

我遇到了同样的问题并写了博客 。

我的同事在Entity Framework 4.0上找到了解决这个问题的非常好的技巧。
适用于smallint,我没试过tinyint。

Insals of equals(==) – 使用通过EF 4.0实现的Contains()运算符。

例如:
说你有专栏SmallIntColumn。

代替:

 short shortValue = 6; var entityList = from r in rep.DataContext.FooTable where r.SmallIntColumn == shortValue select r; 

使用

 short[] shortValue = new short[] { 6 }; var entityList = from r in rep.DataContext.FooTable where shortValue.Contains(r.SmallIntColumn) select r; 

检查生成的SQL – 它现在没有CAST!
从我的测试 – 执行计划完全使用列上的我(过滤)索引。

希望它有所帮助。
施洛米

CAST会影响性能,因为不会在TinyintColumn上使用TinyintColumn

这是“十大常见SQL编程错误”中第 2点和第4点的组合。 CAST是列上的函数,没有它,无论如何都会有数据类型不匹配

@p__linq__0应该是tinyint或明确CAST。

但是,它可能是LINQ不喜欢根据MS Connect和(SO) asp.net mvc linq sql问题的 tinyint主键

你可以“字节”子弹(对不起)并使用smallint …

我发布了我为此问题采取的解决方案。

似乎EntityFramework 4.0总是在tinyint或smallint字段中使用CAST生成查询。 因此,对于性能优化,我决定将这些字段更改为INT以避免CAST,并且我已经更改了其他nvarchar字段的大小,我仍然可以从nvarchar(50)减少到nvarchar(30)。 所以最后我将行的大小从143字节更改为135字节。

如果您具有tinyint的Sql表列数据类型,则相应的POCO对象应具有byte类型的属性。 这对你有用。 除此之外,当您迭代LINQ对象时,它将抛出一个错误,指出无法将字节类型转换为int或您为该属性定义的任何内容。

我刚用EF 4.3 Code First方法validation,一切顺利。

在使用带有lambda表达式的EF时,我遇到了完全相同的问题。 将数据类型添加到int不是解决方案,甚至是不好的做法。 我发现的和其他报道的是,当你采取更笨拙的方法时,你确实得到了正确的代码,例如:

SomeEntity.FindBy(i => new List {1} .Contains(i.TinyintColumn))

但是当你遇到其他问题时,会遇到一个超过一个值的问题。 以下内容不会使用参数化查询值,而只是将它们内联到查询体中!

SomeEntity.FindBy(i => new List {1,2} .Contains(i.TinyintColumn))

这在原始问题上并没有那么糟糕,但仍然不好,因为这意味着数据库必须为您抛出的每个值组合编制一个计划,并且几乎不可能进行性能分析,因为没有适当的执行时间聚合。 它还具有一些您在高负载环境中看不到的性能影响!

不要让我开始研究这些行为/反模式对char / nchar数据类型及其对索引的影响。 正如我所看到的,集中数据类型系统C#implements周围的一切都是有限的,并导致重大问题。

我对EF的看法是,对建模良好的表的非常基本的查询转换为错误的SQL代码,EF遵循反模式。 鉴于炒作以及EF带来的开发复杂性,我发现这并不令人印象深刻! 我现在不会进入那里,因为这将是一个完全不同的讨论!

选择上述任何解决方案,但在使用它们之前要了解其缺点。 也许EF版本10会在一定程度上解决问题,但我不会屏住呼吸。

如果smallint比较是多列上的一个过滤段并且存在与这些列匹配的索引,则DB可能不会优化包含解决方案。 我validation了使用Equals方法使用SmallInt类型解决了这个问题,至少在EF6上。

代替

 short shortValue = 6; var entityList = from r in rep.DataContext.FooTable where r.SmallIntColumn == shortValue select r; 

使用

 short shortValue = 6; var entityList = from r in rep.DataContext.FooTable where r.SmallIntColumn.Equals(shortValue) select r; 

如果您想保留逻辑,可以使用表达式重写方法 。 代码将类似于db.MyEntities.Where(e => e.Id == i).FixIntCast()并保持应用程序逻辑不变。

尝试更复杂的IntCastFixExtension版本:

 namespace System.Linq { ///  /// author: Filip Sielimowicz inspired by /// http://www.entityframework.info/Home/SmallIntProblem ///  public static class IntCastFixExtension { public static IQueryable FixIntCast(this IQueryable q, bool narrowMemberExpr = true, bool narrowConstantExpr = true) { var visitor = new FixIntCastVisitor() { narrowConstExpr = narrowConstantExpr, narrowMembExpr = narrowMemberExpr }; Expression original = q.Expression; var expr = visitor.Visit(original); return q.Provider.CreateQuery(expr); } private class FixIntCastVisitor : ExpressionVisitor { public bool narrowConstExpr; public bool narrowMembExpr; protected override Expression VisitBinary(BinaryExpression node) { bool eq = node.NodeType == ExpressionType.Equal; bool neq = node.NodeType == ExpressionType.NotEqual; if (eq || neq) { var leftUncasted = ReducePossiblyNotNecessaryIntCastExpr(node.Left); var rightUncasted = ReducePossiblyNotNecessaryIntCastExpr(node.Right); var rightConst = node.Right as ConstantExpression; if (leftUncasted == null) { return base.VisitBinary(node); } if (rightUncasted != null) { if (NarrowTypesAreCompatible(leftUncasted.Type, rightUncasted.Type)) { // Usuwamy niepotrzebne casty do intów występujące po obu stronach equalsa return eq ? Expression.Equal(leftUncasted, rightUncasted) : Expression.NotEqual(leftUncasted, rightUncasted); } } else if (rightConst != null) { // Zamiast casta argumentu z lewej w górę do inta (tak zrobił linq2entity) // zawężamy występującą po prawej stałą typu 'int' do typu argumentu z lewej if (narrowConstExpr && (rightConst.Type == typeof(int) || rightConst.Type == typeof(int?))) { var value = rightConst.Value; var narrowedValue = value == null ? null : Convert.ChangeType(rightConst.Value, leftUncasted.Type); Expression narrowedConstExpr = Expression.Constant(narrowedValue, leftUncasted.Type); return eq ? Expression.Equal(leftUncasted, narrowedConstExpr) : Expression.NotEqual(leftUncasted, narrowedConstExpr); } } else if (node.Right.NodeType == ExpressionType.MemberAccess) { // Jak po prawej mamy wyrażenie odwołujące się do zmiennej typu int to robimy podobnie jak przy stałej // - zawężamy to, zamiast upcasta do inta z lewej. if (narrowMembExpr) { var rightMember = node.Right; var narrowedMemberExpr = Expression.Convert(rightMember, leftUncasted.Type); return eq ? Expression.Equal(leftUncasted, narrowedMemberExpr) : Expression.NotEqual(leftUncasted, narrowedMemberExpr); } } } return base.VisitBinary(node); } private bool NarrowTypesAreCompatible(Type t1, Type t2) { if (t1 == typeof(short?)) t1 = typeof(short); if (t2 == typeof(short?)) t2 = typeof(short); if (t1 == typeof(byte?)) t1 = typeof(byte); if (t2 == typeof(byte?)) t2 = typeof(byte); return t1 == t2; } private bool IsNullable(Type t) { return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); } private Expression CorrectNullabilityToNewExpression(Expression originalExpr, Expression newExpr) { if (IsNullable(originalExpr.Type) == IsNullable(newExpr.Type)) { return newExpr; } else { if (IsNullable(originalExpr.Type)) { Type nullableUncastedType = typeof(Nullable<>).MakeGenericType(newExpr.Type); return Expression.Convert(newExpr, nullableUncastedType); } else { Type notNullableUncastedType = Nullable.GetUnderlyingType(newExpr.Type); return Expression.Convert(newExpr, notNullableUncastedType); } } } private Expression ReducePossiblyNotNecessaryIntCastExpr(Expression expr) { var unnecessaryCast = expr as UnaryExpression; if (unnecessaryCast == null || unnecessaryCast.NodeType != ExpressionType.Convert || !(unnecessaryCast.Type == typeof(int) || unnecessaryCast.Type == typeof(int?)) ) { // To nie jest cast na inta, do widzenia return null; } if ( (unnecessaryCast.Operand.Type == typeof(short) || unnecessaryCast.Operand.Type == typeof(byte) || unnecessaryCast.Operand.Type == typeof(short?) || unnecessaryCast.Operand.Type == typeof(byte?)) ) { // Jest cast z shorta na inta return CorrectNullabilityToNewExpression(unnecessaryCast, unnecessaryCast.Operand); } else { var innerUnnecessaryCast = unnecessaryCast.Operand as UnaryExpression; if (innerUnnecessaryCast == null || innerUnnecessaryCast.NodeType != ExpressionType.Convert || !(innerUnnecessaryCast.Type == typeof(int) || innerUnnecessaryCast.Type == typeof(int?)) ) { // To nie jest podwójny cast między intami (np. int na int?), do widzenia return null; } if ( (innerUnnecessaryCast.Operand.Type == typeof(short) || innerUnnecessaryCast.Operand.Type == typeof(byte) || innerUnnecessaryCast.Operand.Type == typeof(short?) || innerUnnecessaryCast.Operand.Type == typeof(byte?)) ) { // Mamy podwójny cast, gdzie w samym środku siedzi short // Robimy skrócenie, żeby intów nie produkował zamiast short -> int -> int? // powinno ostatecznie wychodzić short -> short czyli brak castowania w ogóle. return CorrectNullabilityToNewExpression(unnecessaryCast, innerUnnecessaryCast.Operand); } } return null; } } } 

}