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

Схоже, ви намагаєтесь виконати еквівалент умови "ДЕ ... У". Перегляньте розділ Як писати запити у стилі "ДЕ, ДО", використовуючи LINQ до сутностей, для прикладу того, як робити запити такого типу за допомогою LINQ до сутностей.

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


Спасибі Даніелю. Той самий синтаксис чудово працює з простим Linq. Отже, схоже, проблема в EF у .Net 3.5 SP1, чи не так? Вміщує без дужок еквівалентно: where upperSearchList.All (x => (person.FirstName + person.LastName) .Contens (x)). ToList ();
Gus Cavalcanti

Якщо я спробую, де upperSearchList.All (arg => arg == arg), то виникає та сама помилка. Отже, проблема в методі All ...
Гус Кавальканті,

2
LINQ to Entities - це чудова технологія, але механізм перекладу SQL обмежений. Я не можу покласти руку на офіційну документацію, але на моєму досвіді, якщо запит включає не лише основні математичні та рядкові / датні функції, він не буде працювати. Чи мали ви можливість перевірити цю публікацію, на яку я зв’язав? Він описує процес перетворення запиту типу "WHERE..IN" у форму, яку LINQ у сутності може потім перевести в SQL.
Даніель Пратт,

Ви не можете використовувати покажчики функцій у linq на сутності. Постачальник не знає, як викопати його з дерева виразів, щоб перетворити це на SQL.
Sinaesthetic

@DanielPratt Ваше посилання зламане
Мік

11

Останні 6 місяців я провів боротьбу з цим обмеженням за допомогою EF 3.5, і хоча я не найрозумніший чоловік у світі, я впевнений, що можу запропонувати щось корисне на цю тему.

SQL, згенерований вирощуванням 50-мильного дерева виразів "АБО стиль", призведе до поганого плану виконання запиту. Я маю справу з кількома мільйонами рядків, і вплив є значним.

Існує невеликий хак, який я знайшов, щоб зробити SQL "в", який допомагає, якщо ви просто шукаєте купу сутностей за ідентифікатором:

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. Ендрю Тапперт (Person) має дві посвідчення особи (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 вже не має складного типу "Функція" для роботи, а, наприклад, простого типу (long). І це чудово працює!


0

Я отримав це повідомлення про помилку, коли мій об'єкт масиву, що використовується у функції.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.