LINQ to SQL: повторное использование лямбда-выражения - PullRequest
9 голосов
/ 30 декабря 2010

Я наткнулся на какое-то странное поведение LINQ to SQL - кто-нибудь может пролить свет на это?

Я хочу определить лямбда-выражение и использовать его в своем операторе LINQ. Следующий код работает нормально:

[...]
Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table1s.Where(lambda);
[...]

Но когда я пытаюсь использовать свое лямбда-выражение в выражении для связанной таблицы

[...]
Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda));
[...]

Я получаю исключение:

Unsupported overload used for query operator 'Any'.

Но, и это я не получаю: это работает нормально, когда я помещаю свою лямбду непосредственно в запрос:

[...]
var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000));
[...]

ПОЧЕМУ?!

Спасибо.

Ответы [ 2 ]

19 голосов
/ 30 декабря 2010

Хорошо, вот сделка: dataContext.Table1s имеет тип IQueryable<T>. IQueryable<T> определяет Where и Any методы, которые принимают предикат типа Expression<Func<T, bool>>. Оболочка Expression<> имеет решающее значение, поскольку именно она позволяет LINQ to SQL преобразовывать лямбда-выражение в SQL и выполнять его на сервере базы данных.

Однако IQueryable<T> также включает IEnumerable<T>. IEnumerable<T> также определяет методы Where и Any, но версия IEnumerable принимает предикат типа Func<T, bool>. Поскольку это скомпилированная функция, а не выражение, ее нельзя преобразовать в SQL. В результате этот код ...

Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table1s.Where(lambda);

... извлечет КАЖДУЮ запись из Table1s в память, а затем отфильтрует записи в памяти. Это работает, но это действительно плохие новости, если ваш стол большой.

Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda));

Эта версия имеет два лямбда-выражения. Второй, передаваемый непосредственно в Where, это Expression, который включает ссылку на Func. Вы не можете смешать два, и полученное сообщение об ошибке говорит о том, что вызов Any ожидает Expression, но вы передаете Func.

var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000));

В этой версии ваша внутренняя лямбда автоматически преобразуется в Expression, потому что это единственный выбор, если вы хотите, чтобы ваш код был преобразован в SQL с помощью LINQ to SQL. В других случаях вы заставляете лямбду быть Func вместо Expression - в этом случае это не так, поэтому она работает.

Какое решение? На самом деле все довольно просто:

Expression<Func<Table1, bool>> lambda = x => x.Id > 1000;
0 голосов
/ 30 декабря 2010

Обозначает ли Table1 одно и то же пространство имен?В первом примере вы запрашиваете у Table1 объектов, которые находятся непосредственно под dataContext, во втором примере вы запрашиваете у Table1 объектов, которые являются свойством объектов Table2, а вВ последнем примере вы используете анонимную функцию, которая решает проблему.

Я бы посмотрел тип объекта Table1, который является свойством объекта Table2, и сравнил бы его с объектом Table1, который напрямую связан с dataContext.Я предполагаю, что они отличаются, и ваше лямбда-выражение использует тип объекта, который связан с dataContext.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...