Запутался в передаче аргументов Expression vs. Func - PullRequest
17 голосов
/ 04 января 2012

У меня возникли проблемы с пониманием различий между работой выражений и функций. Эта проблема возникла, когда кто-то изменил сигнатуру метода с:

public static List<Thing> ThingList(Func<Thing, bool> aWhere)

Для

public static List<Thing> ThingList(Expression<Func<Thing, bool>> aWhere)

Который сломал мой код вызова. Старый код вызова (который работал) выглядел так:

        ...
        object y = new object();
        Func<Thing, bool> whereFunc = (p) => p == y;
        things = ThingManager.ThingList(whereFunc);

Новый код (который не работает) выглядит так:

        ...
        object x = new object();
        Expression<Func<Thing, bool>> whereExpr = (p) => p == x;
        things = ThingManager.ThingList(whereExpr);

Сбой внутри ThingList (...) в строке с использованием выражения:

        var query = (from t in context.Things.Where(aWhere)
        ...

С ошибкой во время выполнения:

Unable to create a constant value of type 'System.Object'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.

Этот пример надуманный, но я предполагаю, что он как-то связан с локальной переменной объекта x, которая неправильно «копируется» в выражение.

Может кто-нибудь объяснить, как справиться с этой ситуацией в целом, и почему Func работает, а Expression нет?

Ответы [ 2 ]

13 голосов
/ 04 января 2012

Причина изменения почти наверняка заключалась в том, чтобы "протолкнуть" оценку вашего предиката в базовое хранилище, которое поддерживает ваше context. Вместо того, чтобы переносить все Things в память и затем использовать Func<Thing,bool>, чтобы решить, какие из них оставить, автор измененного API решил использовать IQueryable, и для этого потребовался Expression<Func<Thing,bool>>.

Вы правы относительно источника ошибки: в отличие от предикатов в памяти, IQueryable не может использовать объекты, которые ему не известны, например, произвольные случаи object.

Вам нужно изменить выражение, чтобы избежать ссылки на объекты типов данных, которые не поддерживаются вашим целевым хранилищем данных (я предполагаю, что выражение в конечном итоге попадет либо в Entity Framework, либо в контекст Linq2Sql). Например, вместо того, чтобы сказать

object x = new object();
Expression<Func<Thing, bool>> whereExpr = (p) => p == x;
things = ThingManager.ThingList(whereExpr);

Вы должны сказать

Thing x = new Thing {id = 123};
Expression<Func<Thing, bool>> whereExpr = (p) => p.id == x.id;
things = ThingManager.ThingList(whereExpr);

(ваш резервный магазин почти наверняка понимает целые числа)

6 голосов
/ 04 января 2012

Разница между Expression и Func лучше описана в ответах здесь: Разница между Expression> и Func <>

Чтобы обойти эту работу еще раз, можно скомпилировать выражение обратно в Func.

var query = (from t in context.Things.Where(aWhere.Compile())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...