Linq: Dynamic Query Contruction: запрос перемещается на клиентскую сторону

Я с большим интересом слежу за конверсией здесь:

Создавайте запрос с помощью Linq, а не строк SQL

что касается построения деревьев выражений, где даже имя таблицы является динамическим.

С этой целью я создал метод расширения addWhere, который выглядит так:

static public IQueryable<TResult> addWhere<TResult>(this IQueryable<TResult> query, string columnName, string value)
{
    var providerType = query.Provider.GetType();
    // Find the specific type parameter (the T in IQueryable<T>)
    var iqueryableT = providerType.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
    var tableType = iqueryableT.GetGenericArguments()[0];
    var tableName = tableType.Name;
    var tableParam = Expression.Parameter(tableType, tableName);
    var columnExpression = Expression.Equal(
        Expression.Property(tableParam, columnName),
        Expression.Constant(value));
    var predicate = Expression.Lambda(columnExpression, tableParam);
    var function = (Func<TResult, Boolean>)predicate.Compile();
    var whereRes = query.Where(function);
    var newquery = whereRes.AsQueryable();
    return newquery;
}

[спасибо Timwi за основу этого кода]

Что функционально работает.

Я могу позвонить:

query = query.addWhere("CurUnitType", "ML 15521.1");

и функционально эквивалентен:

query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));

т.е. возвращаемые строки такие же.

Однако я начал смотреть журнал sql и заметил строчку:

query = query.Where(l => l.CurUnitType.Equals("ML 15521.1"));

Сгенерированный запрос:

SELECT (A bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]
WHERE [t0].[CurUnitType] = @p0

тогда как когда я использую линию

query = query.addWhere("CurUnitType", "ML 15521.1");

Сгенерированный запрос:

SELECT (the same bunch of columns)
FROM [dbo].[ObjCurLocView] AS [t0]

Итак, сравнение теперь происходит на стороне клиента, а не добавляется в sql.

Очевидно, это не ахти.

Честно говоря, я в основном вырезал и вставлял код addWhere из примера Timwi (немного другого), так что часть его мне не по зубам. Мне интересно, могу ли я внести какие-либо изменения в этот код, чтобы выражение преобразовывалось в оператор SQL, а не определялось на стороне клиента.

Спасибо, что нашли время, чтобы прочитать это, я приветствую любые комментарии, решения, ссылки и т. Д., Которые могут мне в этом помочь. И, конечно, если я найду решение другими способами, я отправлю ответ здесь.

Ваше здоровье.


person donundeen    schedule 23.09.2010    source источник
comment
Тот факт, что у вас есть Compile, должен быть большим красным флажком, чтобы указать, что он не может работать на сервере БД.   -  person Gabe    schedule 24.09.2010


Ответы (1)


Большая проблема в том, что вы конвертируете дерево выражения в делегат. Посмотрите на подпись Queryable.Where - она ​​выражена в деревьях выражений, а не в делегатах . Итак, вы фактически вызываете Enumerable.Where вместо . Вот почему вам нужно позвонить AsQueryable позже - но здесь этого недостаточно. На самом деле это не возвращает его обратно в область «только деревья выражения внутри», потому что у вас все еще есть делегат. Теперь он завернут в дерево выражений, но вы потеряли детали того, что происходит внутри.

Я подозреваю, что вам нужно следующее:

var predicate = Expression.Lambda<Func<TResult, Boolean>>
      (columnExpression, tableParam);
return query.Where(predicate);

Я с готовностью признаю, что я не читал остальную часть вашего кода, поэтому могут быть другие вещи ... но это основная часть. Вам нужно строго типизированное дерево выражений (отсюда и вызов общей формы Expression.Lambda), которое затем можно передать в Queryable.Where. Дать ему шанс :)

person Jon Skeet    schedule 23.09.2010
comment
Хорошо, я удалил часть компиляции, поэтому теперь код выглядит так: - person donundeen; 24.09.2010