Laden...

MySql.EFCore vs. mysql-datatype 'timestamp'

Erstellt von da_user vor 2 Jahren Letzter Beitrag vor 2 Jahren 657 Views
D
da_user Themenstarter:in
94 Beiträge seit 2008
vor 2 Jahren
MySql.EFCore vs. mysql-datatype 'timestamp'

Verwendetes Datenbanksystem: <MariaDB>

Hallo,

ich versuche mich aus akuten Gründen gerade etwas in das Entity-Framework einzuarbeiten - ich muss auf eine MariaDB zugreifen. Ich halte mich da an das offizielle Tutorial unter Verwendung des Package-Managers: https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework-core-scaffold-example.html#connector-net-entityframework-core-scaffold-packagemgr

Zielframework ist .NET Core 3.1
EF-seitig sind mit NuGet installiert:
MySql.EntityFrameworkCore for Entity Framework 5.0.8+MySQL8.0.27
Microsoft.EntityFrameworkCore.Tools 5.0.11

Wenn ich aus dem letzten Schritt des Tutorials das Scaffolding starte, erhalte ich dabei Fehlermeldungen, dass das EF nicht mit dem Datentyp "timestamp" der Datenbank umgehen kann. Bsp.:> Fehlermeldung:

Could not find type mapping for column '****.last_time' with data type 'timestamp'. Skipping column.
Unable to scaffold the index 'last_time'. The following columns could not be scaffolded: last_time.

Ansonsten läuft das ganze - soweit ich das beurteilen kann - gut durch. Ich habe auch schon etwas weiter gespielt und mir zur Funktionsprüfung div. Datenbankeinträge anzeigen lassen - funktioniert alles, mir fehlt halt die Spalte "last_time" mit den Timestamps.

Ich habe dazu natürlich schon google angeschmissen und bin hierauf gestoßen, laut dem letzten Eintrag seit der Core Version 5.05 funktionieren soll: https://bugs.mysql.com/bug.php?id=102381

Was läuft den hier schief?

16.833 Beiträge seit 2008
vor 2 Jahren

Die korrekte Verwendung von Zeiten ist DateTimeOffset, nicht (mehr) DateTime.
[FAQ] DateTime vs. DateTimeOffset und der Umgang mit Zeiten in .NET

timestamp musst Du manuell mappen über zB
builder
.Entity<XXXX>()
.Property(e => e.last_time)
.HasConversion(time => hier logik);

Hättest auch über die entsprechenden GitHub Issues finden können: https://github.com/dotnet/efcore/issues/6601

D
da_user Themenstarter:in
94 Beiträge seit 2008
vor 2 Jahren

Hallo Abt,

ich komme leider erst am heutigen Montag wieder dazu, da dran zu arbeiten.

Die korrekte Verwendung von Zeiten ist DateTimeOffset, nicht (mehr) DateTime.

In der Datenbank wird der Wert leider als DateTime gespeichert. Daran kann ich auch nix ändern. Wie ich diesen Wert dann natürlich bei mir programmintern weiterverarbeite ist eine andere Geschichte. Und nach dem Studium deines Artikels werde ich wohl DateTime aufs Abstellgleis schieben.

Hättest auch über die entsprechenden GitHub Issues finden können:
>

Hätte ich, wenn ich gewusst hätte, wo ich suchen muss. Aber dass ich in den GitHub Issues suche, noch dazu im eigentlich "falschen" dotnet/efcore, wäre ich nicht gekommen. Bin ja auch nur "Bastelprogrammierer" 😉

Ich habe mich jetzt gerne nach dem letzten Beitrag der Issue-Meldung gerichtet (https://github.com/dotnet/efcore/issues/6601#issuecomment-554773511 ), hatte dabei aber ein paar Probleme. Vielleicht stößt ja jemand anderes per Google oder so auf diesen Thread, darum meine Lösungen:

In der zur Tabelle gehörenden Klasse muss natürlich auch das dazugehörige Feld mit dem Typ DateTime definiert werden:

 public DateTime last_time { get; set; }

Das Property gehört sich in der Datei "sakilaContext.cs" in der Methode "OnModelCreating" gesetzt:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Table1>(entity =>
            {
                entity.ToTable("Table1");
                //---snipp---
                entity.Property(e => e.last_time)
                    .HasColumnName("last_time") //<="last_time" anpassen
                    .HasColumnType("timestamp")
                    .HasConversion(GetDateTimeToDateTimeOffsetConverter());
            });

Der Converter gehört sich in der gleichen Klasse definiert. Ich musste allerdings noch die Verweise auf "ValueConverter" anpassen und den Converter "umdrehen":


        private Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter GetDateTimeToDateTimeOffsetConverter()
        {
            var converter = new Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter<DateTime, DateTimeOffset>(
                                        requested => new DateTimeOffset(requested, TimeSpan.Zero),
                                        requestedWithOffset => requestedWithOffset.DateTime
                                        );

            return converter;
        }

Damit natürlich Danke an Abt, der mich hier mit dem Zaunpfahl auf die richtige Spur gebracht hat.

16.833 Beiträge seit 2008
vor 2 Jahren

Kein Thema.

Nen paar Dinge in EFCore sind etwas komplex bzw. nicht ganz klar beschrieben, wie man da den Weg gehen muss.
Wenn Du wüsstest wie viel Zeit ich in viele Probleme mit EFCore gesteckt hab...mittlerweile weiß man das dann halt.
Muss man durch.

D
da_user Themenstarter:in
94 Beiträge seit 2008
vor 2 Jahren

Na, dann wird noch die eine oder andere Frage kommen, zwei kleinere hätte ich schon in der Pipeline...

Jetzt kommt erstmal eine Korrektur für mein Geschreibsel weiter oben. Wenn man in der zur Tabelle gehörenden Klasse das Feld als "DateTime" definiert, arbeitet man natürlich wieder nicht mit "DateTimeOffset". Das habe ich natürlich korrigiert und musste dann den Converter auch wieder "richtigrum" bauen. Hat dann aber beim Zugriff auf die Werte zu einem Fehler geführt:


                var Fundstellen = SakilaContext.Table1
                    .Where(t => t.colum1 == valueToSearch);
                foreach (var Fundstelle in Fundstellen) //<== hier Fehler

Fehlermeldung:
System.ArgumentException: "Der UTC-Offset des lokalen dateTime-Parameters stimmt nicht mit dem Offset-Argument überein.
Parametername: offset"

Per google gefunden auch als> Fehlermeldung:

DateTimeOffset Error: UTC offset of local dateTime does not match the offset argument datetimeoffset-error-utc-offset-of-local-datetime-does-not-match-the-offset-arg

Ich musste entsprechend den Converter nochmal umbauen. Fertig sieht das ganze dann so aus:

In der zur Tabelle passenden Klasse:

public DateTimeOffset last_time { get; set; }

Das Property gehört sich in der Datei "sakilaContext.cs" in der Methode "OnModelCreating" gesetzt:


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Table1>(entity =>
    {
        entity.ToTable("Table1");
        //---snipp---
        entity.Property(e => e.last_time)
            .HasColumnName("last_time") //≤"last_time" anpassen
            .HasColumnType("timestamp")
            .HasConversion(GetDateTimeToDateTimeOffsetConverter());
    });

Der Converter gehört sich in der gleichen Klasse definiert. Ich musste allerdings noch die Verweise auf "ValueConverter" anpassen und den Converter zwecks DateTimeOffset anpassen:


        private Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter GetDateTimeToDateTimeOffsetConverter()
        {
            var converter = new Microsoft.EntityFrameworkCore.Storage.ValueConversion.ValueConverter<DateTimeOffset, DateTime>(
                                        requestedWithOffset => requestedWithOffset.DateTime,
                                        requested =>  new DateTimeOffset(new DateTime(requested.Ticks, DateTimeKind.Unspecified), TimeZoneInfo.Local.GetUtcOffset(DateTime.Now))
                                        //requested => new DateTimeOffset(requested, TimeSpan.Zero)
                                        );

            return converter;
        }

Bei mir scheinen die Zeiten nach meinen ersten Kontrollen soweit zu passen. GGf. muss man natürlich, je nachdem dass die Zeiten in der jeweilligen Datenbank gespeichert sind, den Offset anpassen (TimeZoneInfo.Local.GetUtcOffset(DateTime.Now)).


So, damit noch zwei Fragen zu EF:

* Gibt es die Möglichkeit die Datenbank auf neu hinzugekommene Werte (die einem bestimmten Filter entsprechen) zu überwachen?
* In einer meiner Tabellen werden bei mir Werte kodiert gespeichert. Diesen Wert muss ich erst in einer anderen Tabelle "Wandlung" suchen, dass ich dann den Reintext bekomme. Diese Tabelle Wandlung ändert sich nicht, bzw. nicht ohne dass ich das mitbekomme. Wäre es jetzt besser sich zu erst diese komplette Tabelle lokal bereitzulegen um Zugriffe auf die Datenbank einzusparen? Oder wird das sowieso optimiert?

D
da_user Themenstarter:in
94 Beiträge seit 2008
vor 2 Jahren

Nachtrag:
ich hätte jetzt gerne meinen Zweiten Beitrag hier in diesem Thread editiert um darauf hinzuweisen, dass meine Problemlösung falsch war. Leider kann ich keine Beiträge editieren. Könnte das ggf. ein Mod für mich übernehmen?