Рассмотрим следующую строку кода:
private void DoThis() {
int i = 5;
var repo = new ReportsRepository<RptCriteriaHint>();
// This does NOT work
var query1 = repo.Find(x => x.CriteriaTypeID == i).ToList<RptCriteriaHint>();
// This DOES work
var query1 = repo.Find(x => x.CriteriaTypeID == 5).ToList<RptCriteriaHint>();
}
Поэтому, когда я вставляю фактическое число в лямбда-функцию, она работает нормально. Когда я использую захваченную переменную в выражении, возвращается следующая ошибка:
No mapping exists from object type ReportBuilder.Reporter+<>c__DisplayClass0 to a known managed provider native type.
Почему? Как я могу это исправить?

С технической точки зрения, правильный способ исправить это - использовать фреймворк, принимающий дерево выражений из лямбда-выражения для оценки ссылки i; другими словами, это ограничение платформы LINQ для какой-то конкретной платформы. В настоящее время он пытается интерпретировать i как членский доступ к некоторому типу, известному ему (поставщику) из базы данных. Из-за того, как работает захват лямбда-переменных, локальная переменная i на самом деле является полем в скрытом классе с забавным именем, которое провайдер не распознает.
Итак, это проблема фреймворка.
Если вам действительно нужно обойтись, вы можете создать выражение вручную, например:
ParameterExpression x = Expression.Parameter(typeof(RptCriteriaHint), "x");
var query = repo.Find(
Expression.Lambda<Func<RptCriteriaHint,bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
x,
typeof(RptCriteriaHint).GetProperty("CriteriaTypeID")),
Expression.Constant(i)),
x)).ToList();
... но это всего лишь мазохизм.
Ваш комментарий к этой записи побуждает меня к дальнейшим объяснениям.
Лямбда-выражения можно преобразовать в один из двух типов: делегат с правильной подписью или Expression<TDelegate> с правильной подписью. LINQ для внешних баз данных (в отличие от любых запросов в памяти) работает с использованием второго типа преобразования.
Компилятор преобразует лямбда-выражения в деревья выражений, грубо говоря, следующим образом:
Expression, так что во время выполнения создается дерево объектов, точно представляющее проанализированный текст.Предполагается, что LINQ для внешних источников данных берет это дерево выражений и интерпретирует его на предмет его семантического содержания, а также интерпретирует символические выражения внутри дерева как относящиеся либо к вещам, специфичным для его контекста (например, столбцы в БД), либо как непосредственные значения для преобразования. Обычно System.Reflection используется для поиска специфичных для платформы атрибутов, направляющих это преобразование.
Однако похоже, что SubSonic неправильно обрабатывает символические ссылки, для которых он не может найти соответствия предметной области; вместо того, чтобы оценивать символические отсылки, это просто пантерация. Таким образом, это проблема SubSonic.
Фреймворк - SubSonic 3.0. Но не следует ли оценивать выражение сравнения перед передачей в метод .Find? Видите, как выражение сравнения представляет собой обычный код на C#?
Нет, выражение сравнения нет оценивается перед передачей.
Спасибо, я свяжусь с автором SubSonic и выясню, почему он не обрабатывает захваченные переменные.
Я обновил свой пост. Фактически, дерево выражений, которое я вручную создаю выше, является приближением того, что происходит для вашего рабочего случая, за исключением того, что вместо Expression.Constant (i) компилятор будет генерировать Expression.Constant (5).
Подтвержденный. Это известная проблема в SubSonic 3, над которой мы будем работать на следующей неделе.
Обновлять. SubSonic 3 Alpha теперь поддерживает захваченные переменные.
Думаю, нет. Я запомню это на будущее ...