Wie vergleiche ich nur Datumskomponenten von DateTime in EF?


116

Ich habe zwei Datumswerte, von denen einer bereits in der Datenbank gespeichert und der andere vom Benutzer mit DatePicker ausgewählt wurde. Der Anwendungsfall besteht darin, in der Datenbank nach einem bestimmten Datum zu suchen.

Der zuvor in die Datenbank eingegebene Wert hat immer eine Zeitkomponente von 12:00:00, wobei das vom Kommissionierer eingegebene Datum eine andere Zeitkomponente hat.

Ich interessiere mich nur für die Datumskomponenten und möchte die Zeitkomponente ignorieren.

Wie kann dieser Vergleich in C # durchgeführt werden?

Wie geht das in LINQ?

UPDATE: Unter LINQ to Entities funktioniert Folgendes einwandfrei.

e => DateTime.Compare(e.FirstDate.Value, SecondDate) >= 0

1
Sie können sich auch diese SO-Frage ansehen
Quintin Robinson

Antworten:


121

HINWEIS: Zum Zeitpunkt des Schreibens dieser Antwort war die EF-Beziehung unklar (die nach dem Schreiben in die Frage bearbeitet wurde). Überprüfen Sie für eine korrekte Vorgehensweise mit EF die Antwort von Mandeeps .


Mit der DateTime.DateEigenschaft können Sie einen Nur-Datum-Vergleich durchführen.

DateTime a = GetFirstDate();
DateTime b = GetSecondDate();

if (a.Date.Equals(b.Date))
{
    // the dates are equal
}

34
Es ist einfach, das Datum zu vergleichen, aber die Frage bezieht sich auf LINQ to Entities, die die .Date-Eigenschaft nicht in SQL konvertieren können.
Michaël Carpentier

1
@ MichaëlCarpentier: guter Punkt. Anscheinend hat es das Problem des OP immer noch gelöst.
Fredrik Mörk

6
Dies fragt nicht die Datenbank ab, sondern verarbeitet die Daten in der CLR / Anwendungsschicht nachträglich. Die eigentliche Lösung besteht darin, die in der folgenden Antwort angegebene Funktion EntityFunctions.TruncateTime (..) zu verwenden, da sie die Abfrage an die Datenbank sendet und die Verarbeitung auf der Speicherebene ermöglicht. Ohne dies könnten Sie die Datumsvergleichslogik in Where / Count-Klauseln nicht verwenden und dann die gefilterten Daten weiter abfragen, da Sie zuerst Teilergebnisse in die Anwendungsschicht ziehen müssten, was in Szenarien, in denen dies der Fall sein kann, ein Deal-Breaker sein kann große Datenmengen verarbeiten.
März

6
@Marchy Ja, auf EntityFunctions.TruncateTimejeden Fall scheint der Weg zu sein in diesen Tagen zu gehen (es wurde in .NET 4 zur Verfügung , die das Jahr veröffentlicht wurde , nachdem diese Frage gestellt wurde).
Fredrik Mörk

1
Verwenden Sie die Methode System.Data.Entity.DbFunctions.TruncateTime (). Sie müssen einen Verweis auf EntityFramework
adeel41

132

Verwenden Sie die Klasse EntityFunctionszum Trimmen des Zeitanteils.

using System.Data.Objects;    

var bla = (from log in context.Contacts
           where EntityFunctions.TruncateTime(log.ModifiedDate) ==  EntityFunctions.TruncateTime(today.Date)
           select log).FirstOrDefault();

Quelle: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/84d4e18b-7545-419b-9826-53ff1a0e2a62/

AKTUALISIEREN

Ab EF 6.0 und höher wird EntityFunctions durch DbFunctions ersetzt .


37
Nur eine Notiz EntityFunctionswurde zugunsten von System.Data.Entity.DbFunctions(mindestens) EF6 abgelehnt. Es könnte früher gewesen sein.
Anfrage

4
Ich würde nicht schnell zu dieser Lösung springen, da sie sehr langsam ist. Weitere Informationen: stackoverflow.com/questions/22776843/…
pajics

Scheint nicht mit einer SQLite-Datenbank zu funktionieren. Ich erhalte "SQL-Logikfehler oder fehlende Datenbank keine solche Funktion: TruncateTime".
Shadowsora

24

Ich denke, das könnte dir helfen.

Ich habe eine Erweiterung vorgenommen, da ich Datumsangaben in mit EF-Daten gefüllten Repositorys vergleichen muss. Daher war das Datum keine Option, da es in der LinqToEntities-Übersetzung nicht implementiert ist.

Hier ist der Code:

        /// <summary>
    /// Check if two dates are same
    /// </summary>
    /// <typeparam name="TElement">Type</typeparam>
    /// <param name="valueSelector">date field</param>
    /// <param name="value">date compared</param>
    /// <returns>bool</returns>
    public Expression<Func<TElement, bool>> IsSameDate<TElement>(Expression<Func<TElement, DateTime>> valueSelector, DateTime value)
    {
        ParameterExpression p = valueSelector.Parameters.Single();

        var antes = Expression.GreaterThanOrEqual(valueSelector.Body, Expression.Constant(value.Date, typeof(DateTime)));

        var despues = Expression.LessThan(valueSelector.Body, Expression.Constant(value.AddDays(1).Date, typeof(DateTime)));

        Expression body = Expression.And(antes, despues);

        return Expression.Lambda<Func<TElement, bool>>(body, p);
    }

dann können Sie es auf diese Weise verwenden.

 var today = DateTime.Now;
 var todayPosts = from t in turnos.Where(IsSameDate<Turno>(t => t.MyDate, today))
                                      select t);

10

Wenn Sie die DateEigenschaft für DB-Entitäten verwenden, erhalten Sie eine Ausnahme:

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

Sie können so etwas verwenden:

  DateTime date = DateTime.Now.Date;

  var result = from client in context.clients
               where client.BirthDate >= date
                     && client.BirthDate < date.AddDays(1)
               select client;

8

Um dies in LINQ to Entities zu tun, müssen Sie unterstützte Methoden verwenden :

var year = someDate.Year;
var month = ...
var q = from r in Context.Records
        where Microsoft.VisualBasic.DateAndTime.Year(r.SomeDate) == year 
              && // month and day

Hässlich, aber es funktioniert und es wird auf dem DB-Server gemacht.


8

Hier ist eine andere Möglichkeit, aber es ist nur nützlich, wenn SecondDate eine Variable ist, die Sie übergeben:

DateTime startDate = SecondDate.Date;
DateTime endDate = startDate.AddDays(1).AddTicks(-1);
...
e => e.FirstDate.Value >= startDate && e.FirstDate.Value <= endDate

Ich denke das sollte funktionieren


1
Ausgezeichnet. Hat für mich gearbeitet. Es war das explizite, was DateTime = x.Date;mir fehlte. Wenn ich varden Wert im Vergleich verwendet habe oder hatte, ist er mit der gemeldeten Ausnahme fehlgeschlagen. Vielen Dank.
Tim Croydon

Ich bin froh, dass es funktioniert hat, Tim. Entschuldigen Sie die Verzögerung bei der Beantwortung. Ich habe mich seit einiger Zeit nicht mehr bei SO angemeldet.
John Kaster

1
Wenn Sie zu wechseln e.FirstDate.Value <= endDate, können e.FirstDate.Value < endDateSie das entfernen .AddTicks(-1).
Marco de Zeeuw

@MarcodeZeeuw du hast recht, das würde definitiv auch funktionieren. Der angezeigte bedingte Ausdruck ist für inklusive Datumsvergleiche der genauen Start- und Enddatenzeiten vorgesehen (vorausgesetzt, die Datumsbereichswerte würden an die Bedingung übergeben und nicht in einem Codefragment eingerichtet). IOW wird die Bedingung als von den Datenzeitwerten getrennt betrachtet .
John Kaster

6

Sie können auch Folgendes verwenden:

DbFunctions.DiffDays(date1, date2) == 0


4

Sie können hierfür die Methode DbFunctions.TruncateTime () verwenden.

e => DbFunctions.TruncateTime(e.FirstDate.Value) == DbFunctions.TruncateTime(SecondDate);

3

Vergleichen Sie einfach immer die Date- Eigenschaft von DateTime anstelle der vollständigen Datums- und Uhrzeitangabe.

Wenn Sie Ihre LINQ-Abfrage durchführen, verwenden Sie date.Date in der Abfrage, dh:

var results = from c in collection
              where c.Date == myDateTime.Date
              select c;

10
Ich erhalte die Fehlermeldung "Das angegebene Typmitglied 'Datum' wird in LINQ to Entities nicht unterstützt. Es werden nur Initialisierer, Entitätsmitglieder und Entitätsnavigationseigenschaften unterstützt." Irgendwelche Gedanken?
Bleistiftscheibe

Ja, Ihr Provider behandelt die .Date-Eigenschaft nicht direkt. Sie müssen es herausziehen und die Daten später vergleichen.
Reed Copsey

.Date kann in Linq To Entities leider nicht verwendet werden. Hoffentlich wird MS diese Überlastungsunterstützung bald hinzufügen
John Kaster

1
Vergleichen Sie immer die Date-Eigenschaft? Ich habe in diesen Kommentar gegoogelt, weil ich mich gefragt habe, ob dies die beste Vorgehensweise ist, d. H. um immer die Date-Eigenschaft zu verwenden, auch wenn es so etwas wie ist candidate.Date >= base.Date. Theoretisch muss die candidate.DateZeit> = 12:00:00 sein, daher ist die Verwendung der Date-Eigenschaft überflüssig, aber ich werde mich an Reeds Rat halten.
Stephen Hosking

3

So mache ich das.

DateTime date_time_to_compare = DateTime.Now;
//Compare only date parts
context.YourObject.FirstOrDefault(r =>
                EntityFunctions.TruncateTime(r.date) == EntityFunctions.TruncateTime(date_to_compare));

2

// Hinweis für Linq Benutzer / Codierer

Dies sollte Ihnen den genauen Vergleich geben, um zu überprüfen, ob ein Datum in den Bereich fällt, wenn Sie mit Eingaben einer Benutzer-Datumsauswahl arbeiten, zum Beispiel:

((DateTime)ri.RequestX.DateSatisfied).Date >= startdate.Date &&
        ((DateTime)ri.RequestX.DateSatisfied).Date <= enddate.Date

Dabei sind Startdatum und Enddatum Werte aus einer Datumsauswahl.


1

Ohne Zeit als versuchen Sie so:

TimeSpan ts = new TimeSpan(23, 59, 59);
toDate = toDate.Add(ts);
List<AuditLog> resultLogs = 
    _dbContext.AuditLogs
    .Where(al => al.Log_Date >= fromDate && al.Log_Date <= toDate)
    .ToList();
return resultLogs;

1

Sie können den folgenden Link verwenden, um 2 Daten ohne Zeit zu vergleichen:

private bool DateGreaterOrEqual(DateTime dt1, DateTime dt2)
        {
            return DateTime.Compare(dt1.Date, dt2.Date) >= 0;
        }

private bool DateLessOrEqual(DateTime dt1, DateTime dt2)
        {
            return DateTime.Compare(dt1.Date, dt2.Date) <= 0;
        }

Die Vergleichsfunktion gibt 3 verschiedene Werte zurück: -1 0 1, was dt1> dt2, dt1 = dt2, dt1 bedeutet


Warum geben Sie nicht einfach DateTime.Compare (dt1.Date, dt2.Date) zurück? Das macht alles was Sie brauchen.
Johnny Graber

0

Versuchen Sie dies ... Es funktioniert gut, Datumseigenschaften zwischen zwei DateTimes-Typen zu vergleichen:

PS. Es ist eine Notlösung und eine wirklich schlechte Praxis. Sie sollte niemals verwendet werden, wenn Sie wissen, dass die Datenbank Tausende von Datensätzen enthalten kann ...

query = query.ToList()
             .Where(x => x.FirstDate.Date == SecondDate.Date)
             .AsQueryable();

1
PS: Normalerweise verwende ich diese Methode, wenn die DateTimes den Wert Time haben und ich nur das Datum vergleichen möchte.
Raskunho

2
Dies ist eine sehr schlechte Lösung. Die Abfrage ruft alle Datensätze ab und filtert erst dann die Daten heraus. Wenn die Datenbank Millionen von Datensätzen enthält, werden alle erfasst und erst dann werden die Daten gefiltert. Sehr schlechte Praxis.
Dementic

1
Es ist eine Notlösung und eine wirklich schlechte Praxis. Sie sollte niemals verwendet werden, wenn Sie wissen, dass die Datenbank Tausende von Datensätzen enthalten kann.
Raskunho

Wenn Sie Ihren Kommentar zu Ihrer Antwort hinzufügen, werde ich meine Abwahl entfernen. Jedem, der diese Seite besucht, sollte klar sein, dass die von Ihnen vorgeschlagene Lösung schlecht ist, ohne die Kommentare lesen zu müssen.
Dementic

Obwohl dieser Ansatz im Allgemeinen eine schlechte Idee ist, führt er zu einer erheblich verbesserten Leistung für kleine Datensatzgruppen (<1000 Datensätze oder so), da EF Datumsvergleiche dumm in SQL übersetzt. Ich habe gesehen, dass Abfragen von über einer Minute auf unter eine Sekunde verschoben wurden, indem nur der Datumsvergleich im Speicher durchgeführt wurde, anstatt in dem, was SQL EF generiert.
Extragorey
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.