Entity Framework - ошибка «Невозможно создать постоянное значение типа« Тип закрытия »…»

79

Почему я получаю ошибку:

Невозможно создать постоянное значение типа «Тип закрытия». В этом контексте поддерживаются только примитивные типы (например, Int32, String и Guid).

Когда я пытаюсь перечислить следующий запрос Linq?

IEnumerable<string> searchList = GetSearchList();
using (HREntities entities = new HREntities())
{
   var myList = from person in entities.vSearchPeople
   where upperSearchList.All( (person.FirstName + person.LastName) .Contains).ToList();
}

Обновление : если я попробую следующее, чтобы попытаться изолировать проблему, я получаю ту же ошибку:

where upperSearchList.All(arg => arg == arg) 

Похоже, проблема в методе All, не так ли? Какие-либо предложения?

Гас Кавальканти
источник

Ответы:

68

Похоже, вы пытаетесь выполнить эквивалент условия "WHERE ... IN". Посмотрите, как писать запросы в стиле «WHERE IN» с использованием LINQ to Entities, чтобы получить пример того, как выполнять этот тип запроса с LINQ to Entities.

Кроме того, я думаю, что сообщение об ошибке в этом случае особенно бесполезно, потому что за .Containsним не следуют круглые скобки, что заставляет компилятор распознавать весь предикат как лямбда-выражение.

Дэниел Пратт
источник
Спасибо, Даниэль. Тот же синтаксис отлично работает с простым Linq. Итак, похоже, проблема с EF в .Net 3.5 SP1, верно? Содержимое без скобок эквивалентно: where upperSearchList.All (x => (person.FirstName + person.LastName) .Contains (x)). ToList ();
Гас Кавальканти
Если я попробую, где upperSearchList.All (arg => arg == arg), он выдает ту же ошибку. Итак, проблема в методе All ...
Гас Кавальканти
2
LINQ to Entities - замечательная технология, но механизм перевода SQL ограничен. Я не могу найти официальную документацию, но по моему опыту, если запрос включает в себя больше, чем просто основные математические функции и функции строки / даты, он не будет работать. У вас была возможность посмотреть этот пост, на который я указал? Он описывает процесс преобразования запроса типа «WHERE..IN» в форму, которую LINQ to Entities может затем преобразовать в SQL.
Дэниел Пратт
Вы не можете использовать указатели на функции в linq для сущностей. Провайдер не знает, как извлечь его из дерева выражения, чтобы преобразовать его в SQL.
Sinaesthetic
@DanielPratt, твоя ссылка не работает
Мик
11

Последние 6 месяцев я боролся с этим ограничением с помощью EF 3.5, и хотя я не самый умный человек в мире, я почти уверен, что у меня есть что-то полезное по этой теме.

SQL, сгенерированный путем выращивания дерева выражений в стиле «ИЛИ» высотой 50 миль, приведет к плохому плану выполнения запроса. Я имею дело с несколькими миллионами строк, и это оказывает существенное влияние.

Есть небольшой прием, который я нашел для выполнения SQL "in", который помогает, если вы просто ищете группу сущностей по идентификатору:

private IEnumerable<Entity1> getByIds(IEnumerable<int> ids)
{
    string idList = string.Join(",", ids.ToList().ConvertAll<string>(id => id.ToString()).ToArray());
    return dbContext.Entity1.Where("it.pkIDColumn IN {" + idList + "}");
}

где pkIDColumn - это имя столбца идентификатора вашего первичного ключа в таблице Entity1.

НО ПРОДОЛЖАЙТЕ ЧИТАТЬ!

Это нормально, но для этого у меня уже есть идентификаторы того, что мне нужно найти. Иногда я просто хочу, чтобы мои выражения коснулись других отношений, и у меня есть критерии для этих связанных отношений.

Если бы у меня было больше времени, я бы попытался изобразить это визуально, но я не так просто изучаю это предложение на мгновение: рассмотрим схему с таблицами Person, GovernmentId и GovernmentIdType. Эндрю Тапперт (человек) имеет две идентификационные карты (GovernmentId), одну из Орегона (GovernmentIdType) и одну из Вашингтона (GovernmentIdType).

Теперь сгенерируйте из него edmx.

Теперь представьте, что вы хотите найти всех людей, имеющих определенное значение идентификатора, скажем, 1234567.

Это может быть выполнено одним обращением к базе данных следующим образом:

dbContext context = new dbContext();
string idValue = "1234567";
Expression<Func<Person,bool>> expr =
    person => person.GovernmentID.Any(gid => gid.gi_value.Contains(idValue));

IEnumerable<Person> people = context.Person.AsQueryable().Where(expr);

Вы видите здесь подзапрос? Сгенерированный sql будет использовать «соединения» вместо подзапросов, но эффект тот же. В наши дни SQL-сервер все равно оптимизирует подзапросы в соединения, но в любом случае ...

Ключом к этой работе является .Any внутри выражения.

Андрей
источник
8

Я нашел причину ошибки (я использую Framework 4.5). Проблема в том, что EF - сложный тип, который передается в параметре «Содержит», не может быть преобразован в запрос SQL. EF может использовать в запросе SQL только простые типы, такие как int, string ...

this.GetAll().Where(p => !assignedFunctions.Contains(p))

GetAll предоставляет список объектов сложного типа (например, «Функция»). Поэтому я бы попытался получить здесь экземпляр этого сложного типа в моем SQL-запросе, который, естественно, не может работать!

Если я могу извлечь из своего списка параметры, которые подходят для моего поиска, я могу использовать:

var idList = assignedFunctions.Select(f => f.FunctionId);
this.GetAll().Where(p => !idList.Contains(p.FunktionId))

Теперь у EF больше нет сложного типа «Функция» для работы, но, например, с простым типом (длинным). И это прекрасно работает!

Питер70
источник
0

Я получил это сообщение об ошибке, когда мой объект массива, используемый в функции .All, имеет значение null. После инициализации объекта массива (в вашем случае upperSearchList) ошибка исчезла. В этом случае сообщение об ошибке вводило в заблуждение.

где upperSearchList.All (arg => person.someproperty.StartsWith (arg)))

анг
источник