Entity Framework - Fehler "Konstanter Wert vom Typ" Schließungstyp "kann nicht erstellt werden ..."

79

Warum bekomme ich den Fehler:

Es kann kein konstanter Wert vom Typ 'Verschlusstyp' erstellt werden. In diesem Zusammenhang werden nur primitive Typen (z. B. Int32, String und Guid) unterstützt.

Wenn ich versuche, die folgende Linq-Abfrage aufzulisten?

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

Update : Wenn ich Folgendes versuche, um das Problem einzugrenzen, wird der gleiche Fehler angezeigt:

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

Es sieht also so aus, als ob das Problem bei der All-Methode liegt, oder? Irgendwelche Vorschläge?

Gus Cavalcanti
quelle

Antworten:

68

Es sieht so aus, als würden Sie versuchen, das Äquivalent einer "WHERE ... IN" -Zustand zu tun. Check out Wie schreibt man ‚WHERE IN‘ Stil Abfragen LINQ to Entities verwenden für ein Beispiel dafür , wie Unternehmen , die Art der Abfrage mit LINQ zu tun.

Ich denke auch, dass die Fehlermeldung in diesem Fall besonders wenig hilfreich ist, da .Containskeine Klammern folgen, wodurch der Compiler das gesamte Prädikat als Lambda-Ausdruck erkennt.

Daniel Pratt
quelle
Danke Daniel. Die gleiche Syntax funktioniert gut mit einfachem Linq. Es scheint also ein Problem mit EF in .Net 3.5 SP1 zu sein, oder? Der Inhalt ohne Klammer entspricht: wobei UpperSearchList.All (x => (person.FirstName + person.LastName) .Contains (x)). ToList ();
Gus Cavalcanti
Wenn ich versuche, UpperSearchList.All (arg => arg == arg), wird der gleiche Fehler ausgegeben. Das Problem liegt also bei der All-Methode ...
Gus Cavalcanti
2
LINQ to Entities ist eine wunderbare Technologie, aber die SQL-Übersetzungs-Engine ist begrenzt. Ich kann die offizielle Dokumentation nicht in die Hände bekommen, aber meiner Erfahrung nach wird die Abfrage nicht funktionieren, wenn sie mehr als nur grundlegende Mathematik- und Zeichenfolgen- / Datumsfunktionen enthält. Haben Sie die Gelegenheit gehabt, den von mir verlinkten Beitrag zu lesen? Es beschreibt einen Prozess zum Konvertieren einer Abfrage vom Typ "WHERE..IN" in ein Formular, das LINQ in Entities dann in SQL übersetzen kann.
Daniel Pratt
Sie können in linq keine Funktionszeiger auf Entitäten verwenden. Der Anbieter weiß nicht, wie er es aus einem Ausdrucksbaum ausgraben soll, um es in SQL zu konvertieren.
Sinaesthetic
@ DanielPratt dein Link ist kaputt
Mick
11

Ich habe die letzten 6 Monate damit verbracht, diese Einschränkung mit EF 3.5 zu bekämpfen, und obwohl ich nicht die klügste Person der Welt bin, bin ich mir ziemlich sicher, dass ich zu diesem Thema etwas Nützliches zu bieten habe.

Die SQL, die durch das Wachsen eines 50 Meilen hohen Baums von Ausdrücken im "OR-Stil" generiert wird, führt zu einem schlechten Ausführungsplan für Abfragen. Ich habe es mit ein paar Millionen Zeilen zu tun und die Auswirkungen sind erheblich.

Es gibt einen kleinen Hack, den ich gefunden habe, um ein SQL-In auszuführen, das hilft, wenn Sie nur nach einer Reihe von Entitäten anhand der ID suchen:

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

Dabei ist pkIDColumn der Name Ihrer Primärschlüssel-ID-Spalte in Ihrer Entity1-Tabelle.

ABER LESEN SIE WEITER!

Das ist in Ordnung, aber es erfordert, dass ich bereits die IDs von dem habe, was ich finden muss. Manchmal möchte ich nur, dass meine Ausdrücke in andere Beziehungen hineinreichen, und was ich habe, sind Kriterien für diese verbundenen Beziehungen.

Wenn ich mehr Zeit hätte, würde ich versuchen, dies visuell darzustellen, aber ich studiere diesen Satz nicht nur einen Moment lang: Betrachten Sie ein Schema mit den Tabellen Person, GovernmentId und GovernmentIdType. Andrew Tappert (Person) hat zwei ID-Karten (GovernmentId), eine aus Oregon (GovernmentIdType) und eine aus Washington (GovernmentIdType).

Generieren Sie nun einen edmx daraus.

Stellen Sie sich nun vor, Sie möchten alle Personen mit einem bestimmten ID-Wert finden, z. B. 1234567.

Dies kann mit einem einzigen Datenbanktreffer erreicht werden:

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

Sehen Sie die Unterabfrage hier? Die generierte SQL verwendet 'Joins' anstelle von Unterabfragen, aber der Effekt ist der gleiche. Heutzutage optimiert SQL Server Unterabfragen ohnehin in Joins unter dem Deckmantel, aber trotzdem ...

Der Schlüssel zu dieser Arbeit ist das .Any im Ausdruck.

Andrew
quelle
8

Ich habe die Fehlerursache gefunden (ich verwende Framework 4.5). Das Problem ist, dass EF, ein komplexer Typ, der im Parameter "Enthält" übergeben wird, nicht in eine SQL-Abfrage übersetzt werden kann. EF kann in einer SQL-Abfrage nur einfache Typen wie int, string ... verwenden.

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

GetAll bietet eine Liste von Objekten mit einem komplexen Typ (zum Beispiel: "Funktion"). Daher würde ich hier versuchen, eine Instanz dieses komplexen Typs in meiner SQL-Abfrage zu erhalten, die natürlich nicht funktionieren kann!

Wenn ich aus meiner Liste Parameter extrahieren kann, die für meine Suche geeignet sind, kann ich Folgendes verwenden:

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

Jetzt hat EF nicht mehr den komplexen Typ "Funktion" zum Arbeiten, sondern zB mit einem einfachen Typ (lang). Und das funktioniert gut!

peter70
quelle
0

Ich habe diese Fehlermeldung erhalten, wenn mein in der .All-Funktion verwendetes Array-Objekt null ist. Nachdem ich das Array-Objekt initialisiert habe (in Ihrem Fall die obere Suchliste), ist der Fehler verschwunden. Die Fehlermeldung war in diesem Fall irreführend

Dabei ist UpperSearchList.All (arg => person.someproperty.StartsWith (arg)))

ang
quelle