Linq to EntityFramework DateTime

108

In meiner Anwendung verwende ich Entity Framework.

Mein Tisch

-Article
-period
-startDate

Ich brauche Datensätze, die übereinstimmen => DateTime.Now > startDate and (startDate + period) > DateTime.Now

Ich habe diesen Code ausprobiert, aber er funktioniert jetzt

Context.Article
    .Where(p => p.StartDate < DateTime.Now)
    .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now)

Wenn ich meinen Code ausführe, tritt die folgende Ausnahme auf

LINQ to Entities erkennt die Methode 'System.DateTime AddDays (Double)' nicht und diese Methode kann nicht in einen Speicherausdruck übersetzt werden.

Yucel
quelle
Welcher Typ ist period? AddDaysist die falsche Funktion, wenn es eine ist double.
Craig Stuntz

Antworten:

201

Bei Verwendung von LINQ to Entity Framework werden Ihre Prädikate in der Where-Klausel in SQL übersetzt. Sie erhalten diesen Fehler, weil es keine DateTime.Add()sinnvolle Übersetzung in SQL gibt.

Eine schnelle Lösung besteht darin, die Ergebnisse der ersten Where-Anweisung in den Speicher zu lesen und dann LINQ to Objects zu verwenden, um die Filterung abzuschließen:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .ToList()
               .Where(p => p.StartDate.AddDays(p.Period) > DateTime.Now);

Sie können auch die EntityFunctions.AddDays- Methode ausprobieren , wenn Sie .NET 4.0 verwenden:

Context.Article.Where(p => p.StartDate < DateTime.Now)
               .Where(p => EntityFunctions.AddDays(p.StartDate, p.Period)
                   > DateTime.Now);

Hinweis: EF 6Es ist jetzt System.Data.Entity.DbFunctions.AddDays.

Justin Niessner
quelle
42
Dies ist eine gefährliche Problemumgehung. Was passiert, wenn ToList () eine große Datenmenge zurückgibt?
Stefan P.
7
Vielen Dank für den Tipp zu EntityFunctions.AddDays. Ich verwende EF .net 4.0, wusste aber nichts über EntityFunctions.
Stefan P.
2
@SaeedAlg - Im ersten Beispiel müssen die Elemente in den Speicher eingelesen werden. Im zweiten Beispiel habe ich zwei Where-Klauseln beibehalten, die dem Originalformat entsprechen. Selbst wenn Sie die beiden in eine einzige Where-Klausel komprimieren, generiert Entity Framework Identitäts-SQL, sodass es wirklich um die Lesbarkeit geht.
Justin Niessner
2
@ Justin Niessner vielen Dank, ich versuche das für vier Stunden zu tun, Sie retten mein Leben in Sekunden nochmals
vielen
2
Wenn wir mit EF "schnelle" Problemumgehungen durchführen, sollten wir alle die Verwendung von ToList- und ToArray-Methoden vermeiden. Es ist genauso schnell, das vorherige Datum zu berechnen und mit diesem Wert zu vergleichen. Dies funktioniert in einer EF-Abfrage, ohne die Ergebnisse im Speicher zu instanziieren.
Isaac Llopis
92

Ich denke, dies ist das, was diese letzte Antwort vorschlagen wollte, aber anstatt zu versuchen, p.startdat Tage hinzuzufügen (etwas, das nicht in eine SQL-Anweisung konvertiert werden kann), warum nicht etwas tun, das mit SQL gleichgesetzt werden kann:

var baselineDate = DateTime.Now.AddHours(-24);

something.Where(p => p.startdate >= baselineDate)
Andrew
quelle
2
@Adrian Carr - Es kann für diesen Anwendungsfall besser sein, aber nicht für so viele wie die EntityFunctionsLösung. Hier wird der zweite Operand nicht von einer anderen Entität in der Abfrage abgerufen und kann vor der Abfrage berechnet werden. Wenn beide Operanden in db gefunden würden, wäre die EntityFunctionsLösung immer noch geeignet, während die Lösung dieser Antwort nicht mehr funktionieren würde.
Frédéric
Dies ist eine bessere Lösung als die als Antwort gekennzeichnete Lösung. Die Antwort von Justin / markierte Antwort gibt unnötige Ergebnisse aus der Datenbank zurück. Diese Lösung sendet tatsächlich das Datum in SQL, wo gefiltert wird, und verwendet SQL, um die Daten herauszufiltern, was viel leistungsfähiger ist.
Paul
3

Wie wäre es, wenn Sie 2 Tage von DateTime abziehen. Jetzt:

Context.Article
.Where(p => p.StartDate < DateTime.Now)
.Where(p => p.StartDate > DateTime.Now.Subtract(new TimeSpan(2, 0, 0, 0)))

Um ehrlich zu sein, bin ich mir nicht sicher, was Sie erreichen wollen, aber das könnte funktionieren

TimC
quelle
Artikel werden am Startdatum angezeigt und enden nach x (Zeitraum) Tagen. Das versuche ich zu tun. Justin Niessners zweite Lösung funktionierte sehr gut für das, was ich sehen möchte
Yucel
3

Wenn Sie möchten, dass Ihr Ausdruck in SQL übersetzt wird, können Sie versuchen, ihn zu verwenden

System.Data.Entity.Core.Objects.AddDays-Methode.

Eigentlich ist veraltet markiert, aber es funktioniert. Es sollte durch System.Data.Entity.DbFunctions.AddDays ersetzt werden, aber ich kann es nicht finden ...

Bubi
quelle
Dies fügt nichts weiter als die akzeptierte Antwort von 4 Jahren zuvor hinzu!
Andrew Harris
@ AndrewHarris auch meine Antwort ist vor 4 Jahren Antwort. In der ersten Version der Antwort gab es eine ToList (=> Datenmaterialisierung) vor dem Where (siehe den ersten Kommentar der Antwort).
Bubi