LINQ und DateTime

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    LINQ und DateTime

    Hallo,

    ich hadere gerade mit der Welt. Folgendes funktioniert nicht. Ich erhalte keine Datensätze zurück, zu erwarten sind 50.


    C#-Quellcode

    1. public List<GiroEntry> DownloadGiro(string datumStart, string datumEnde) {
    2. List<GiroEntry> res = null;
    3. DateTime? start = DateTime.Parse("01.02.2021");
    4. DateTime? ende = DateTime.Parse("28.02.2021");
    5. try {
    6. using (KontoEntities db = new KontoEntities()) {
    7. res = db.tblGiro.Where(x => x.Buchungstag >= start)
    8. .Where(x => x.Buchungstag <= ende).ToList();
    9. }
    10. }
    11. catch (Exception ex) {
    12. err.Message = ex.Message;
    13. err.Methode = MethodInfo.GetCurrentMethod().Name;
    14. err.Modul = this.GetType().Name;
    15. //err.Show();
    16. }
    17. return res;
    18. }


    Einzelheiten:

    Datenbank: PostgreSQL
    Entity Framework 6 (Npgsql)
    .Net Framework 4.7
    Typ von "Buchungstag" in Datenbank: date
    Typ von Buchungstag in C#: DateTime?
    Lasse ich die Where-Clauses weg, erhalte ich alle Datensätze aus der entsprechenden Tabelle und ich kann auch nach anderen Parameter (z.B. dem Primärschlüssel) filtern. Alles geht.


    Geht man ins Netz und sucht, findet man, dass LINQ to Entity kein DateTime kennt. Trotzdem meckert der Compiler nicht. Die Beispiele im Netz nennen DbFunctions.TruncateTime um vom DateTime den Zeitteil abzuspalten. Ich vergleiche aber keine Strings sondern echte(?) DateTime und die Bedingungen sind auch kein "gleich" sondern "größer als". Rundungsfehler oder ähnliches sollten hier also kein Problem sein. Alles was ich im Netz bisher finden konnte bringt mich nicht weiter. Es bleibt dabei, es werden keine Datensätze zurückgegeben.

    Warum macht LINQ to Entity hier solche Mucken und wie sieht ein Workround aus? Das Filtern nach Datumse ist doch eine mehr als übliche Anforderungen an Datenbank-Abfragen.

    Gruß

    MQ

    PS.: Das Konstrukt funktioniert übrigens unter .NetCore. daraus habe ich es kopiert und war erstmal überrascht, dass es unter .NET Framework nicht geht.
    Hallo,
    ich habe zum testen mal was ähnliches nachgebaut:

    C#-Quellcode

    1. ​static void Main(string[] args)
    2. {
    3. var startingDate = new DateTime(2021, 1, 1);
    4. var endingDate = new DateTime(2021, 1, 31);
    5. using (var context = new AppDbContext())
    6. {
    7. context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
    8. var result = context.Accounts.Where(a => a.PostingDate >= startingDate)
    9. .Where(a => a.PostingDate <= endingDate).ToList();
    10. foreach (var a in result)
    11. {
    12. Console.WriteLine($"{a.Id} - {a.PostingDate}");
    13. }
    14. }
    15. Console.ReadLine();
    16. }


    Er baut mir korrekt folgendes Statement

    SQL-Abfrage

    1. ​SELECT
    2. [Extent1].[Id] AS [Id],
    3. [Extent1].[PostingDate] AS [PostingDate]
    4. FROM [dbo].[Accounts] AS [Extent1]
    5. WHERE ([Extent1].[PostingDate] >= @p__linq__0) AND ([Extent1].[PostingDate] <= @p__linq__1)

    -- p__linq__0: '01.01.2021 00:00:00' (Type = DateTime2, IsNullable = false)

    -- p__linq__1: '31.01.2021 00:00:00' (Type = DateTime2, IsNullable = false)
    Ich erhalte 2 Ergebnisse von 5 Datensätzen in der Tabelle. Ich habe hier leider kein Postgre am Start, aber grundsätzlich sollte dein Code so funktionieren. Evtl. loggst du mal dein SQL-Statement. Vielleicht findest du dort einen Hinweis, warum du keine Ergebnisse bekommst. Evtl. macht der Postgre-Treiber die "zicken".
    Bilder
    • Screenshot 2021-03-28 221748.png

      2 kB, 434×198, 63 mal angesehen
    Moin,

    Ich habe es gerade mit dem SQL-Server probiert und das klappt einwandfrei.

    Was mich dabei allerdings wundert ist, dass ich es auch probiert hatte, die Where-Klausel auf die List anzuwenden und nicht auf die Entities direkt.

    C#-Quellcode

    1. res = db.tblGiro.ToList().Where(x => x.Buchungstag >= start)
    2. .Where(x => x.Buchungstag <= ende).ToList();


    Und das tut's bei PostgreSQL auch nicht.

    hmm ....
    Oder erstmal nur mit einer atomaren Bedingung, um das Teil von grundauf zu testen.

    C#-Quellcode

    1. var result = db.tblGiro.Where(x => x.Buchungstag >= start).ToList()
    Denn da scheint ja was anders zu laufen als man annimmt.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Dann lass Dir doch erstmal überhaupt einen Buchungstag ausgeben. In welchem Format ist es denn abgespeichert?
    Also:

    C#-Quellcode

    1. var FirstTimeStamp = db.tblGiro.First.Buchungstag
    Was kommt da überhaupt raus?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Moment. Nur zur Sicherheit: Was passiert, wenn Du start und ende nicht als nullables definierst?

    C#-Quellcode

    1. DateTime start = DateTime.Parse("01.02.2021");
    2. DateTime ende = DateTime.Parse("28.02.2021");

    Und: hast Du Einträge, die in die angegebene Zeitspanne passen?
    Was kommt hier raus:

    C#-Quellcode

    1. var Condition = db.tblGiro.Select(x => x.Buchungstag).Any(y => y >= start);
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    MasterQ schrieb:

    Wie komme ich denn an den SQL-Code?

    Ist doch hier vorgemacht:

    ISliceUrPanties schrieb:

    C#-Quellcode

    1. context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
    wenn das mit PostGree funktioniert.

    etwas eleganter wäre:

    C#-Quellcode

    1. context.Database.Log = System.Diagnostics.Debug.WriteLine;
    SQL:

    SQL-Abfrage

    1. SELECT "Extent1"."Buchungstag" FROM "public"."tblGiro" AS "Extent1" WHERE "Extent1"."Buchungstag" >= @p__linq__0 AND "Extent1"."Buchungstag" <= @p__linq__1


    Ausgabefenster:

    -- p__linq__0: '01.02.2021 00:00:00' (Type = DateTime, IsNullable = false)
    -- p__linq__1: '28.02.2021 00:00:00' (Type = DateTime, IsNullable = false)
    -- Executing at 29.03.2021 14:25:17 +02:00
    -- Completed in 22 ms with result: NpgsqlDataReader

    ist OK!

    DateTime? vs DateTime
    keine Unterschiede

    .SELECT().ANY()
    gibt false. Im Testintervall des letzten Februar liegen rund 50 Datensätze, die ich mit anderen Tools, z.B. pgAdmin, abfragen kann.

    Der Vergleich der Datumsangaben funktioniert nicht, warum auch immer.

    PostgreSQL kennt den Typ DateTime nicht sondern date. Der .NET-Treiber stellt einen eigenen Typ NpgsqlTypes.NpgsqlDate zur Verfügung. Der ModelGenerator hat beim Import daraus ein DateTime? gemacht. Ich erhalte auf den ersten Blick korrekte DateTime gecastet, trotzdem funktioniert der Vergleich nicht. Laut IntelliSense vergleiche ich DateTime? mit DateTime? und eben nicht NpgsqlDate mit DateTime. Bei letzterem könnte es zu Inkompatibilitäten kommen, bei ersterem aber nicht.

    Das was ohne LinQ ausgelesen wird, stimmt mit den Daten auf dem Server überein.

    Das Problem ist keines des PostgreSQL Treibers sondern von C#. Das Datenfeld aus der Entität GiroEntry vom Typ DateTime? vergleicht sich nicht mit den Variablen start und ende vom Typ DateTime?. Ich habe auch schon händisch zwischen NpgsqlDate und DateTime gecastet, gebracht hat's nix.


    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „MasterQ“ ()

    Es ist genau der Code vom ersten Post, mit Ausnahme jetzt, dass kein Nullable verwendet wurde.


    Was mir jetzt grade auffällt ist, dass beim SQL-Server Beispiel (Post #2) von ISliceUrPanties linq als Typ in der SQL-Ausgabe DateTime2 angibt. Das ist der Typ vom SQL-Server und nicht von C#. Bei mir ist's DateTime, also nicht der Typ vom Server sondern von C#. Ob das was zu sagen hat?

    MasterQ schrieb:

    Es ist genau der Code vom ersten Post

    Ok - die Methode returnt ja eine List<GiroEntry>.
    In meiner Welt müsste jedes in dieser List enthaltene GiroEntry eine Property DateTime BuchungsTag haben.

    Da tät ich denken, sowas müsste gehen:

    C#-Quellcode

    1. public List<GiroEntry> DownloadGiro(string datumStart, string datumEnde) {
    2. List<GiroEntry> res = null;
    3. DateTime? start = DateTime.Parse("01.02.2021");
    4. DateTime? ende = DateTime.Parse("28.02.2021");
    5. try {
    6. using (KontoEntities db = new KontoEntities()) {
    7. res = db.tblGiro.Where(x => x.Buchungstag >= start)
    8. .Where(x => x.Buchungstag <= ende).ToList();
    9. }
    10. }
    11. catch (Exception ex) {
    12. err.Message = ex.Message;
    13. err.Methode = MethodInfo.GetCurrentMethod().Name;
    14. err.Modul = this.GetType().Name;
    15. //err.Show();
    16. }
    17. MessageBox.Show(res.Count==0 ? "leer" : res[0].BuchungsTag.ToString());
    18. return res;
    19. }

    Man kann die wheres auch mit && verbinden.

    C#-Quellcode

    1. res = db.tblGiro.Where(x => x.Buchungstag >= start && x.Buchungstag <= ende).ToList();


    Ändert aber nix.

    Obige Zeile kompiliert zu

    C#-Quellcode

    1. res = db.tblGiro.Where((GiroEntry x) => (DateTime)x.Buchungstag >= (DateTime)(DateTime?)start && (DateTime)x.Buchungstag <= (DateTime)(DateTime?)ende).ToList();


    Das sieht auch komisch aus mit den Doppelcasts!
    Guten Morgen,

    zunächst möchte ich mich für meine Blödheit entschuldigen. Alles ist gut und der Code macht was er soll.

    Das Problem lag am ConnectionString, der den falschen Server ansprach. Auf diesem liegt eine Sicherungskopie der DB, die dem Stand vom Ende letzten Jahres entspricht und somit keine Daten vom Februar enthält.

    Heute morgen ist mir langsam gedämmert, dass das Problem an was ganz anderem liegen muss.

    Sry für die Unannehmlichkeiten.

    MQ