Entity Framework - błąd „Nie można utworzyć stałej wartości typu„ Typ zamknięcia ”…”

79

Dlaczego otrzymuję błąd:

Nie można utworzyć stałej wartości typu „Typ zamknięcia”. W tym kontekście obsługiwane są tylko typy pierwotne (na przykład Int32, String i Guid).

Kiedy próbuję wyliczyć następujące zapytanie 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();
}

Aktualizacja : Jeśli spróbuję wykonać następujące czynności, aby spróbować wyodrębnić problem, pojawia się ten sam błąd:

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

Więc wygląda na to, że problem dotyczy metody All, prawda? Jakieś sugestie?

Gus Cavalcanti
źródło

Odpowiedzi:

68

Wygląda na to, że próbujesz uzyskać odpowiednik warunku „GDZIE ... W”. Sprawdź, jak pisać zapytania w stylu „WHERE IN” przy użyciu LINQ to Entities, aby zapoznać się z przykładem sposobu wykonywania tego typu zapytania za pomocą LINQ to Entities.

Uważam też, że komunikat o błędzie jest szczególnie nieprzydatny w tym przypadku, ponieważ .Containsnie jest poprzedzony nawiasami, co powoduje, że kompilator rozpoznaje cały predykat jako wyrażenie lambda.

Daniel Pratt
źródło
Dzięki Daniel. Ta sama składnia działa dobrze ze zwykłym Linq. Więc wygląda na to, że problem jest z EF w .Net 3.5 SP1, prawda? Pole Contains bez nawiasów jest równoważne z: where upperSearchList.All (x => (person.FirstName + person.LastName) .Contains (x)). ToList ();
Gus Cavalcanti
Jeśli spróbuję gdzie upperSearchList.All (arg => arg == arg) generuje ten sam błąd. Więc problem jest z metodą All ...
Gus Cavalcanti
2
LINQ to Entities to wspaniała technologia, ale silnik tłumaczenia SQL jest ograniczony. Nie mogę położyć rąk na oficjalnej dokumentacji, ale z mojego doświadczenia wynika, że ​​jeśli zapytanie obejmuje więcej niż tylko podstawowe funkcje matematyczne i funkcje ciągów / daty, to nie zadziała. Czy miałeś okazję sprawdzić ten post, do którego dołączyłem link? Opisuje proces konwertowania zapytania typu „WHERE..IN” na formularz, który LINQ to Entities może następnie przetłumaczyć na język SQL.
Daniel Pratt
Nie można używać wskaźników funkcji w linq do encji. Dostawca nie wie, jak wykopać to z drzewa wyrażeń, aby przekonwertować je na SQL.
Sinaesthetic
@DanielPratt Twój link jest uszkodzony
Mick
11

Spędziłem ostatnie 6 miesięcy walcząc z tym ograniczeniem za pomocą EF 3.5 i chociaż nie jestem najmądrzejszą osobą na świecie, jestem prawie pewien, że mam coś pożytecznego do zaoferowania na ten temat.

Kod SQL wygenerowany przez powiększenie drzewa wyrażeń „w stylu OR” o wysokości 50 mil spowoduje słaby plan wykonania zapytania. Mam do czynienia z kilkoma milionami wierszy, a wpływ jest znaczący.

Znalazłem mały hack dotyczący wykonywania kodu SQL `` in '', który pomaga, jeśli szukasz tylko kilku jednostek według identyfikatora:

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 + "}");
}

gdzie pkIDColumn to nazwa kolumny identyfikatora klucza podstawowego tabeli Entity1.

ALE CZYTAJCIE!

To jest w porządku, ale wymaga posiadania już identyfikatorów tego, co muszę znaleźć. Czasami po prostu chcę, aby moje wyrażenia sięgały do ​​innych relacji, a to, co mam, to kryteria tych powiązanych relacji.

Gdybym miał więcej czasu, spróbuję przedstawić to wizualnie, ale nie przestudiuję tego zdania przez chwilę: rozważ schemat z tabelami Person, GovernmentId i GovernmentIdType. Andrew Tappert (osoba) ma dwie karty identyfikacyjne (GovernmentId), jedną z Oregonu (GovernmentIdType) i jedną z Waszyngtonu (GovernmentIdType).

Teraz wygeneruj z niego edmx.

Teraz wyobraź sobie, że chcesz znaleźć wszystkie osoby o określonej wartości identyfikacyjnej, powiedzmy 1234567.

Można to osiągnąć za pomocą pojedynczego trafienia w bazie danych w ten sposób:

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);

Czy widzisz tutaj podzapytanie? Wygenerowany sql użyje „złączeń” zamiast pod-zapytań, ale efekt jest taki sam. W dzisiejszych czasach serwer SQL i tak optymalizuje podzapytania pod kątem łączenia pod osłonami, ale i tak ...

Kluczem do tej pracy jest .Any wewnątrz wyrażenia.

Andrzej
źródło
8

Znalazłem przyczynę błędu (używam Framework 4.5). Problem polega na tym, że EF typ złożony, który jest przekazywany w parametrze „Contains”, nie może zostać przetłumaczony na zapytanie SQL. EF może używać w zapytaniach SQL tylko prostych typów, takich jak int, string ...

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

GetAll zawiera listę obiektów o typie złożonym (na przykład: „Funkcja”). Dlatego spróbuję tutaj otrzymać wystąpienie tego złożonego typu w moim zapytaniu SQL, które naturalnie nie może działać!

Jeśli mogę wyodrębnić z mojej listy parametry, które pasują do mojego wyszukiwania, mogę użyć:

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

Teraz EF nie ma już do pracy typu złożonego „Function”, ale np. Z typem prostym (long). I to działa dobrze!

peter70
źródło
0

Otrzymałem ten komunikat o błędzie, gdy mój obiekt tablicy używany w funkcji .All ma wartość null Po zainicjowaniu obiektu tablicy (w twoim przypadku upperSearchList) błąd zniknął Komunikat o błędzie był w tym przypadku mylący

gdzie upperSearchList.All (arg => person.someproperty.StartsWith (arg)))

ang
źródło