.NET LINQ IQueryable: для чего нужен `Expression`?

Я работаю над поставщиком LINQ, поэтому внедряю IQueryable.

Какова цель свойства Expression в этом интерфейсе? Обычно я просто возвращаю что-то вроде Expression.Constant(this) из своих реализаций, но понятия не имею, плохо ли это.

Как ни странно в документации говорится " Это позволяет платформе различать запросы LINQ и Entity SQL ».


person Ian Newson    schedule 20.05.2015    source источник
comment
Попробуйте посмотреть, что присутствует в EF или LINQ-to-SQL в этом свойстве ...   -  person xanatos    schedule 20.05.2015
comment
Насколько я помню, свойство Expression содержит выражение для текущего IQueryable, поскольку у нас может быть их цепочка, поэтому каждое IQueryable имеет свои собственные и ранее сгруппированные выражения. Но могу ошибаться, так как использую очень давно.   -  person Sergey Litvinov    schedule 20.05.2015
comment
@SergeyLitvinov Да, я знаю, что это один из возможных способов его использования, но насколько мне известно, фреймворк не требует его такого использования. Так могут ли исполнители делать с этим свойством как им заблагорассудится?   -  person Ian Newson    schedule 20.05.2015


Ответы (1)


Член IQueryable.Expression возвращается в IQueryProvider всеми различными Queryable.* «операторами» (.Where(), .Select(), .Join(), ...), например:

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) {
    if (source == null)
        throw Error.ArgumentNull("source");
    if (predicate == null)
        throw Error.ArgumentNull("predicate");
    return source.Provider.CreateQuery<TSource>( 
        Expression.Call(
            null,
            ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource)), 
            new Expression[] { source.Expression, Expression.Quote(predicate) }
            ));
}

(взято из справочного источника)

Обычно это должно быть все выражение.

Ясно, что никто не убьет вас, если вы передадите напрямую всю ссылку на ваш IQueryable класс через Expression.Constant(), но я действительно думаю, что это не «кошерно».

Смысл помещения "настоящего" выражения в Expression (как это делается Enumerable.AsQueryable(), EF и LINQ-to-SQL, просто чтобы назвать трех IQueryable поставщиков) заключается в том, что другие внешние классы могут свободно анализировать и манипулировать Expression и передать его провайдеру так же, как это делает Queryable.Where, при этом

Expression expression = source.Expression;
// here "expression" is manipulated
return source.Provider.CreateQuery<SomeType>(expression);

Чтобы привести пример ... Существуют некоторые "средства исправления запросов", которые изменяют запрос, например https://github.com/davidfowl/QueryInterceptor (общий модификатор для запросов) или https://github.com/hazzik/DelegateDecompiler, позволяющий:

var employees = (from employee in db.Employees
                 where employee.FullName == "Test User"
                 select employee).Decompile().ToList();

где FullName не отображается в БД и является таким свойством, как:

class Employee
{
    [Computed]
    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }

FirstName и LastName, сопоставленными с базой данных). DelegateDecompiler берет Expression из IQueryable, ищет свойства с атрибутом Computed, декомпилирует их и помещает декомпилированный код (преобразованный в дерево выражений) обратно в IQueryable.Expression (хотя используется IQueryable.Provider.CreateQuery())

Если вы хотите сохранить дополнительные данные, вы можете поместить их в Provider: вы можете создать новый экземпляр класса IQueryProvider в методе CreateQuery. Это тоже возвращается операторами Queryable.* (потому что CreateQuery<> является методом экземпляра source.Provider)

person xanatos    schedule 20.05.2015
comment
Спасибо за подробный ответ. В качестве небольшого дополнительного вопроса: является ли относительно распространенным использование свойства Expression для попытки изменить, дублировать или иным образом проверить запросы? Я никогда не видел этого на практике и мог бы предположить, что это очень неизвестная «особенность». - person Ian Newson; 20.05.2015
comment
О, и моя реализация действительно предоставляет другие свойства, которые позволяют проверять настроенный запрос, поэтому Expression.Constant(this) не так глуп, как может показаться на первый взгляд. - person Ian Newson; 20.05.2015
comment
@IanNewson Я бы сказал, что это редко встречается у программистов, редко у писателей библиотек. - person xanatos; 20.05.2015