Entity Framework - смешной запрос, отбрасывающий smallint для int для сравнения

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 строк из нескольких миллиардов в этой таблице).

Принятый ответ

Такое поведение может быть распространено для разных поставщиков LINQ, а не только для EF-специфики, поскольку компилятор C # генерирует дерево выражений для выражения Where.

Когда вы укажете условие как:

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

и оба BucketRef и bucketId являются шортами, компилятор генерирует от коротких до int для обеих частей сравнения, потому что оператор == не определен для типа Short. Это объясняется в ответ на https://stackoverflow.com/a/18584429/869184

В качестве обходного пути вы можете переписать условие следующим образом:

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

Это эффективно удаляет отливку из сравнения.


Популярные ответы

Вы должны сами создать выражение Where используя статические функции класса Expression .

Как это:

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 , чтобы удалить этот VariantNo == set




Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему
Лицензировано согласно: CC-BY-SA with attribution
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему