我正在查询tinyint列,实体框架生成一个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?或者我做错了什么?
提前致谢。
答案 0 :(得分:11)
如果您将IList<T>.Contains
与List<byte>
一起使用,则不会投射实体框架。
List<byte> byteValue = new List<byte> { 6 };
var entityList = from r in rep.DataContext.FooTable
where byteValue.Contains(r.TinyintColumn)
select r;
我遇到了同样的问题和blogged about it。
答案 1 :(得分:5)
我的同事在Entity Framework 4.0上找到了解决这个问题的非常好的技巧 适用于smallint,我没试过tinyint。
Equals 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!
从我的测试中 - 执行计划在列上完全使用了我的(过滤的)索引。
希望它有所帮助。
施洛米
答案 2 :(得分:2)
CAST会影响性能,因为索引不会在TinyintColumn
这是"Ten Common SQL Programming Mistakes"中第2点和第4点的组合。 CAST是列上的函数,如果没有它,您无论如何都会出现数据类型不匹配
@p__linq__0
应该是tinyint或明确CAST。
然而,根据MS Connect和(SO)asp.net mvc linq sql problem
,LINQ可能不像tinyint主键你可以&#34;字节&#34;子弹(抱歉)并使用smallint ......
答案 3 :(得分:2)
如果smallint比较是多列上的过滤的一个段并且存在与这些列匹配的索引,则DB可能不会优化包含解决方案。我确认使用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;
答案 4 :(得分:1)
我正在发布我为此问题采取的解决方案。
似乎EntityFramework 4.0总是在tinyint或smallint字段中使用CAST生成查询。因此,对于性能优化,我决定将这些字段更改为INT以避免CAST,并且我已经更改了其他nvarchar字段的大小,我仍然可以将其从nvarchar(50)减少到nvarchar(30)。所以最后我将行的大小从143字节更改为135字节。
答案 5 :(得分:1)
如果您的Sql表列数据类型为tinyint,则相应的POCO对象应具有byte类型的属性。这对你有用。除此之外,当您迭代LINQ对象时,它会抛出一个错误,指出无法将字节类型转换为int或者您为该属性定义的任何内容。
我刚刚使用EF 4.3 Code First Approach验证,一切顺利。
答案 6 :(得分:1)
在使用带有lambda表达式的EF时遇到了完全相同的问题。将数据类型添加到int不是解决方案,甚至是不好的做法。我发现和其他报道的是,当你采取更笨拙的方法时,你会得到正确的代码,例如:
SomeEntity.FindBy(i =&gt; new List {1} .Contains(i.TinyintColumn))
但是当你遇到其他问题时,会遇到一个以上匹配的值。以下内容不会使用参数化查询值,而只是将它们内联到查询体中!
SomeEntity.FindBy(i =&gt; new List {1,2} .Contains(i.TinyintColumn))
这在原始问题上并没有那么糟糕,但仍然不好,因为这意味着数据库必须为您抛出的每个值组合编制一个计划,并且由于没有适当的聚合,因此几乎不可能进行性能分析执行时间。它还具有一些性能影响,你宁可在高负载环境中看不到它们!
不要让我开始研究这些行为/反模式对char / nchar数据类型及其对索引的影响。正如我所看到的,集中数据类型系统C#implements周围的一切都是有限的并且会导致重大问题。
我对EF的看法是,对建模良好的表的非常基本的查询转换为错误的SQL代码,EF遵循反模式。鉴于炒作以及EF带来的开发复杂性,我发现这并不令人印象深刻!我现在不会进入那里,因为这将是一个完全不同的讨论!
选择上述任何解决方案,但在使用它们之前要了解其缺点。也许EF版本10会在一定程度上解决问题,但我不会屏住呼吸。
答案 7 :(得分:0)
如果您想保留逻辑,可以使用expression rewrite method。 代码就像 db.MyEntities.Where(e =&gt; e.Id == i).FixIntCast() 并保持应用程序逻辑不变。
答案 8 :(得分:0)
尝试更复杂的IntCastFixExtension版本:
namespace System.Linq {
/// <summary>
/// author: Filip Sielimowicz inspired by
/// http://www.entityframework.info/Home/SmallIntProblem
/// </summary>
public static class IntCastFixExtension {
public static IQueryable<T> FixIntCast<T>(this IQueryable<T> 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<T>(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;
}
}
}
}
答案 9 :(得分:0)
db列可能为空。
试试这个:r.TinyintColumn.Value == byteValue