Линк где ключевое слово против где расширение и параметры выражения - PullRequest
6 голосов
/ 03 марта 2010

Передача выражения в запрос Linq ведет себя по-разному в зависимости от используемого синтаксиса, и мне интересно, почему это так.

Допустим, у меня есть эта очень общая функция

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)

Следующая реализация работает как ожидалось

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return (from c in _ctx.Companies.Where(whereClause) select c);
}

Но эта следующая реализация не компилируется (Делегат System.Func не принимает 1 аргумента)

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return (from c in _ctx.Companies where whereClause select c);
}

Очевидно, я могу просто использовать первый синтаксис, но мне просто интересно, почему компилятор не рассматривает ключевое слово where так же, как расширение Where?

Спасибо, Томас

Ответы [ 3 ]

3 голосов
/ 03 марта 2010

Синтаксис для выражения запроса, включающего предложение where, (упрощение полной грамматики)

from identifier in expression where boolean-expression select expression

whereClause не является логическим выражением. Чтобы исправить это, вы должны сказать

from c in _ctx.Companies where whereClause.Compile()(c) select c;

Обратите внимание, что если бы whereClause было Func<Company, bool>, вы могли бы обойтись без

from c in _ctx.Companies where whereClause(c) select c;

Обратите внимание, что

from x in e where f

механически переводится компилятором в

(from x in e).Where(x => f)

Я говорю механически, потому что он выполняет этот перевод без какого-либо семантического анализа для проверки правильности вызовов методов и т. Д. Этот этап наступает позже, после того как все выражения запроса были преобразованы в выражения вызова метода LINQ.

В частности,

from c in _ctx.Companies where whereClause select c

переводится как

_ctx.Companies.Where(c => whereClause).Select(c)

что явно бессмысленно.

Причина, по которой

from c in _ctx.Companies.Where(whereClause) select c

допустимо, потому что IEnumerable<Company>.Where имеет перегрузку, принимающую Func<Company, bool>, и существует неявное преобразование из Expression<Func<Company, bool>> в Func<Company, bool>.

2 голосов
/ 03 марта 2010

Вы можете сократить все это до:

private IEnumerable<Company> 
    GetCompanies(Expression<Func<Company, bool>> whereClause)
{
    return _ctx.Companies.Where(whereClause);
}

Когда вы используете синтаксис LINQ, код в предложении where преобразуется в Expression<>, который представляет собой дерево кодов. Когда вы принимаете Expression<Func<Customer, bool>>, вы говорите, что ваш метод принимает дерево кода, которое преобразуется из кода C # компилятором.

Поскольку у вас уже есть дерево кодов, вы должны передать его непосредственно методу Where(), а не использовать синтаксис LINQ.

0 голосов
/ 03 марта 2010

Разница в том, что в sql-like, где он ожидает выражение, которое оценивается как bool. Но в методе Where тип выражения может быть делегатом.

, чтобы заставить секунд работать, вы можете изменить на whereClause.Compile()(c) или изменить параметр на Func<Company, bool> whereClause и whereClause(c)

...