Określony typ elementu członkowskiego „Data” nie jest obsługiwany w wyjątku LINQ to Entities

105

Wystąpił wyjątek podczas wdrażania poniższych instrukcji.

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

Wyjątek

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

Wiem, co oznacza wyjątek, ale nie wiem, jak się go pozbyć. Jakaś pomoc?

mgławica
źródło
To jest w EF6 i niższych. Obsługuje rdzeń EF .Date.
Gert Arnold

Odpowiedzi:

102

LINQ to Entities nie może przetłumaczyć większości metod .NET Date (w tym użytego rzutowania) na SQL, ponieważ nie ma odpowiednika SQL.

Rozwiązaniem jest użycie metod Date poza instrukcją LINQ, a następnie przekazanie wartości. Wygląda na to, że błąd powoduje Convert.ToDateTime (rule.data) .Date.

Wywołania Date we właściwości DateTime również nie można przetłumaczyć na język SQL, więc obejściem jest porównanie właściwości .Year .Month i .Day, które można przetłumaczyć na LINQ, ponieważ są to tylko liczby całkowite.

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);
Dżudo
źródło
6
A co z j => j.JobDeadline.Date?
mgławica
1
Czy Date jest właściwością w JobDeadline? To nie powinno samo w sobie powodować błędu - być może konflikt nazw (ale nie jestem tego pewien). Jeśli linia nadal powoduje błąd, po prostu zmień jej nazwę DeadlineDate lub podobną.
Judo
1
Date jest właściwością JobDeadline. JobDeadline to typ DateTime, z którego chcę wyodrębnić Date.
mgławica
Następnie, aby pracować z LINQ, wystarczy porównać właściwość JobDeadline, np. J.JobDeadline> ruleDate. To wymaga trochę testów, ale może zadziałać. Możesz też porównać trzy właściwości .Month .Day i .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Nie eleganckie, ale działa, ponieważ są to tylko liczby całkowite.
Judo
Hmm. Ten pomysł działa. Brudne, ale działa. Jeśli napiszesz to jako odpowiedź, mogę oznaczyć to jako poprawne.
mgławica
230

Możesz użyć metody TruncateTime EntityFunctions, aby uzyskać poprawne tłumaczenie Datewłaściwości na SQL:

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


Aktualizacja: EntityFunctions jest przestarzała w EF6, użyjDbFunctions.TruncateTime

Slauma
źródło
Cóż, zauważyłem, że ruleDatajest to DateTimetyp i j.JobDeadlineskrócony czas. To nie jest w porządku. Nie dostałem wyjątku, ale nie uzyskałem również oczekiwanego rezultatu.
mgławica
@aneal: zwraca wszystkie rekordy, które JobDeadlinemają tę samą datę co rule.data, bez względu na porę dnia . Czy to nie jest to, co chcesz osiągnąć za pomocą pytania w pytaniu? Dlaczego nie wydaje się to właściwe?
Slauma,
1
+1 i zgadzam się z powyższym, to zdecydowanie lepsza odpowiedź na 99% wdrożeń
Jim Tollan
26
Zauważ, że EntityFunctionsjest przestarzałe w EF6, powinieneś teraz używać DbFunctions.
Julien N,
2
Przestrzeń nazw dla funkcji DbFunctions w> EF6 to System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF
39

W przypadku EF6 zamiast tego użyj DbFunctions.TruncateTime (mydate).

KingOfHypocrites
źródło
9

„EntityFunctions.TruncateTime” lub „DbFunctions.TruncateTime” w ef6 Działa, ale występuje problem z wydajnością w Big Data.

Myślę, że najlepszym sposobem jest takie zachowanie:

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

jest to lepsze niż używanie części daty do. ponieważ zapytanie działa szybciej w przypadku dużych ilości danych.

Mahdi Shahbazi
źródło
+1 za tę odpowiedź. EntityFunctions.TruncateTime(później zastąpione przez DbFunctions.TruncateTime) są implementowane przez konwersję do SQL, w którym data / godzina jest konwertowana na ciąg i jest obcinana. To sprawia, że ​​zapytanie działa znacznie wolniej, proporcjonalnie do liczby przetwarzanych rekordów.
urig
3

Musisz dołączyć using System.Data.Entity;. Działa dobrze nawet zProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);
Omkar
źródło
Jak już tu
Gert Arnold
1

Oznacza to, że LINQ to SQL nie wie, jak zamienić Datewłaściwość na wyrażenie SQL. Dzieje się tak, ponieważ Datewłaściwość DateTimestruktury nie ma odpowiednika w języku SQL.

Adam Robinson
źródło
1

U mnie to zadziałało.

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

Źródło: fora Asp.net

MRT2017
źródło
0

Mam ten sam problem, ale pracuję z zakresami dat i godzin. Moim rozwiązaniem jest manipulowanie czasem początkowym (z dowolną datą) na 00:00:00 i czasem końcowym na 23:59:59, więc nie mogę już dłużej konwertować daty i godziny na datę, a raczej pozostanie to data i godzina.

Jeśli masz tylko jedną datę i godzinę, możesz również ustawić godzinę rozpoczęcia (z dowolną datą) na 00:00:00 i godzinę zakończenia na 23:59:59. Następnie wyszukujesz tak, jakby był to przedział czasu.

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

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

Możesz to zrobić również z DateTime-Range:

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

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);
peter70
źródło
0

możesz dostać Enum na przykład:

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
źródło
To skutecznie wylicza całą zawartość tabeli z wyprzedzeniem w celu zastosowania filtru daty ... wydaje się być potencjalnie bardzo złym pomysłem!
Javier Rapoport
0

Jak wielu tutaj zauważyło, korzystanie z funkcji TruncateTime jest powolne.

Najłatwiejszą opcją, jeśli możesz, jest użycie EF Core. Potrafi to zrobić. Jeśli nie możesz, to lepszą alternatywą dla obcinania jest nie zmienianie w ogóle pola, którego dotyczy zapytanie, ale zmodyfikowanie jego granic. Jeśli wykonujesz zwykłe zapytanie typu „pomiędzy”, w którym dolna i górna granica są opcjonalne, poniższe wskazówki załatwią sprawę.

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

Zasadniczo zamiast przycinać PoDate z powrotem tylko do części Date, zwiększamy górną granicę zapytania i user <zamiast <=

statler
źródło