Das angegebene Typmitglied 'Date' wird in LINQ to Entities Exception nicht unterstützt

105

Ich habe bei der Implementierung der folgenden Anweisungen eine Ausnahme erhalten.

 DateTime result;
 if (!DateTime.TryParse(rule.data, out result))
     return jobdescriptions;
 if (result < new DateTime(1754, 1, 1)) // sql can't handle dates before 1-1-1753
     return jobdescriptions;
 return jobdescriptions.Where(j => j.JobDeadline.Date == Convert.ToDateTime(rule.data).Date );

Ausnahme

The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Ich weiß, was die Ausnahme bedeutet, aber ich weiß nicht, wie ich sie loswerden soll. Irgendeine Hilfe?

Nebel
quelle
Dies ist in EF6 und niedriger. EF-Kern unterstützt .Date.
Gert Arnold

Antworten:

102

LINQ to Entities kann die meisten .NET Date-Methoden (einschließlich des von Ihnen verwendeten Castings) nicht in SQL übersetzen, da es kein gleichwertiges SQL gibt.

Die Lösung besteht darin, die Date-Methoden außerhalb der LINQ-Anweisung zu verwenden und dann einen Wert zu übergeben. Es sieht so aus, als ob Convert.ToDateTime (rule.data) .Date den Fehler verursacht.

Das Aufrufen von Date für eine DateTime-Eigenschaft kann ebenfalls nicht in SQL übersetzt werden. Daher besteht eine Problemumgehung darin, die Eigenschaften .Year .Month und .Day zu vergleichen, die in LINQ übersetzt werden können, da es sich nur um Ganzzahlen handelt.

var ruleDate = Convert.ToDateTime(rule.data).Date;
return jobdescriptions.Where(j => j.Deadline.Year == ruleDate.Year 
                       && j.Deadline.Month == ruleDate.Month 
                       && j.Deadline.Day == ruleDate.Day);
Judo
quelle
6
Was ist mit j => j.JobDeadline.Date?
Nebel
1
Ist Date eine Eigenschaft in JobDeadline? Dies sollte selbst keinen Fehler verursachen - möglicherweise einen Namenskonflikt (aber nicht sicher). Wenn die Zeile immer noch einen Fehler verursacht, benennen Sie sie einfach in DeadlineDate oder ähnliches um.
Judo
1
Datum ist Eigentum von JobDeadline. JobDeadline ist der DateTime-Typ, dessen Datum ich extrahieren möchte.
Nebel
Um dies in LINQ zu tun, müssen Sie nur die JobDeadline-Eigenschaft vergleichen, z. B. j.JobDeadline> ruleDate. Dies erfordert einige Tests, kann aber zum Funktionieren gebracht werden. Vergleichen Sie alternativ die drei Eigenschaften von .Month .Day und .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Nicht elegant, aber es funktioniert, da dies nur ganze Zahlen sind.
Judo
Hmm. Diese Idee funktioniert. Schmutzig aber funktioniert. Wenn Sie es als Antwort schreiben, kann ich es richtig markieren.
Nebel
230

Sie können die TruncateTime- Methode der EntityFunctions verwenden , um eine korrekte Übersetzung der DateEigenschaft in SQL zu erzielen :

using System.Data.Objects; // you need this namespace for EntityFunctions

// ...

DateTime ruleData = Convert.ToDateTime(rule.data).Date;
return jobdescriptions
    .Where(j => EntityFunctions.TruncateTime(j.JobDeadline) == ruleData);


Update: EntityFunctions ist in EF6, Verwendung veraltetDbFunctions.TruncateTime

Slauma
quelle
Nun, ich habe bemerkt, dass ruleDatadas DateTimeTyp ist und j.JobDeadlinedie Zeit verkürzt hat. Fühlt sich nicht richtig an. Keine Ausnahme, aber auch kein erwartetes Ergebnis.
Nebel
@aneal: Es werden alle Datensätze zurückgegeben, bei denen JobDeadlinedas gleiche Datum wie angezeigt wird rule.data, unabhängig von der Tageszeit . Ist es nicht das, was Sie mit Ihrer Anfrage in der Frage erreichen wollen? Warum fühlt es sich nicht richtig an?
Slauma
1
+1 und ich stimme oben zu, ist definitiv eine bessere Antwort für 99% der Implementierungen
Jim Tollan
26
Beachten Sie, dass dies EntityFunctionsin EF6 veraltet ist und Sie es jetzt verwenden sollten DbFunctions.
Julien N
2
Namespace für DbFunctions in> EF6 ist System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF
39

Verwenden Sie für EF6 stattdessen DbFunctions.TruncateTime (mydate).

KingOfHypocrites
quelle
9

"EntityFunctions.TruncateTime" oder "DbFunctions.TruncateTime" in ef6 funktioniert, es gibt jedoch einige Leistungsprobleme in Big Data.

Ich denke, der beste Weg ist, so zu handeln:

DateTime ruleDate = Convert.ToDateTime(rule.data);

DateTime  startDate = SearchDate.Date;

DateTime  endDate = SearchDate.Date.AddDay(1);

return jobdescriptions.Where(j.Deadline >= startDate 
                       && j.Deadline < endDate );

Es ist besser, als Teile des Datums zu verwenden. weil die Abfrage in großen Datenmengen schneller ausgeführt wird.

Mahdi Shahbazi
quelle
+1 für diese Antwort. EntityFunctions.TruncateTime(später ersetzt durch DbFunctions.TruncateTime) werden durch Konvertieren in SQL implementiert, wobei die datetime in eine Zeichenfolge konvertiert und diese abgeschnitten wird. Dadurch wird die Abfrage im Verhältnis zur Anzahl der verarbeiteten Datensätze erheblich langsamer ausgeführt.
Urig
3

Müssen enthalten using System.Data.Entity;. Funktioniert auch gut mitProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);
Omkar
quelle
Wie schon hier
Gert Arnold
1

Dies bedeutet, dass LINQ to SQL nicht weiß, wie die DateEigenschaft in einen SQL-Ausdruck umgewandelt werden kann. Dies liegt daran, dass die DateEigenschaft der DateTimeStruktur in SQL kein Analogon hat.

Adam Robinson
quelle
1

Es hat bei mir funktioniert.

DateTime dt = DateTime.Now.Date;
var ord = db.Orders.Where
      (p => p.UserID == User && p.ValidityExpiry <= dt);

Quelle: Asp.net-Foren

MRT2017
quelle
0

Ich habe das gleiche Problem, arbeite aber mit DateTime-Ranges. Meine Lösung besteht darin, die Startzeit (mit einem beliebigen Datum) auf 00:00:00 und die Endzeit auf 23:59:59 zu ändern. Daher muss ich meine DateTime nicht mehr in Date konvertieren, sondern sie bleibt DateTime.

Wenn Sie nur eine DateTime haben, können Sie auch die Startzeit (mit einem beliebigen Datum) auf 00:00:00 und die Endzeit auf 23:59:59 einstellen. Dann suchen Sie, als wäre es eine Zeitspanne.

var from = this.setStartTime(yourDateTime);
var to = this.setEndTime(yourDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);

Sie können dies auch mit DateTime-Range tun:

var from = this.setStartTime(yourStartDateTime);
var to = this.setEndTime(yourEndDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);
peter70
quelle
0

Sie können Enum wie folgt bekommen:

DateTime todayDate = DateTime.Now.Date; var check = db.tableName.AsEnumerable().Select(x => new
        {
            Date = x.TodayDate.Date
        }).Where(x => x.Date == todayDate).FirstOrDefault();
Omid Soleiman
quelle
Dies zählt effektiv den gesamten Inhalt der Tabelle im Voraus auf, um den Datumsfilter anzuwenden ... scheint möglicherweise eine sehr schlechte Idee zu sein!
Javier Rapoport
0

Wie viele hier bereits betont haben, ist die Verwendung der TruncateTime-Funktion langsam.

Die einfachste Option, wenn Sie können, ist die Verwendung von EF Core. Das kann es. Wenn Sie dies nicht können, besteht eine bessere Alternative zum Abschneiden darin, das abgefragte Feld überhaupt nicht zu ändern, sondern die Grenzen zu ändern. Wenn Sie eine normale Abfrage vom Typ "Zwischen" ausführen, bei der die Unter- und Obergrenze optional sind, reicht Folgendes aus.

    public Expression<Func<PurchaseOrder, bool>> GetDateFilter(DateTime? StartDate, DateTime? EndDate)
    {
        var dtMinDate = (StartDate ?? SqlDateTime.MinValue.Value).Date;
        var dtMaxDate = (EndDate == null || EndDate.Value == SqlDateTime.MaxValue.Value) ? SqlDateTime.MaxValue.Value : EndDate.Value.Date.AddDays(1);
        return x => x.PoDate != null && x.PoDate.Value >= dtMinDate && x.PoDate.Value < dtMaxDate;
    }

Anstatt PoDate auf den Datumsteil zurückzuschneiden, erhöhen wir grundsätzlich die obere Abfragegrenze und den Benutzer <anstelle von <=

statler
quelle