Laden...

Was ist eine vernünftige Konvertierung für DateTime von SQL zu C# und zurück?

Erstellt von kolibri0769 vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.963 Views
K
kolibri0769 Themenstarter:in
4 Beiträge seit 2020
vor 3 Jahren
Was ist eine vernünftige Konvertierung für DateTime von SQL zu C# und zurück?

Hallo an Alle,

ich "nerve" hier mal mit einem wahrscheinlich uralten Thema, aber ich habe die Ideal-Lösung bisher nicht gefunden:

Es geht um die Konvertierung von Datum/Zeit zwischen SQL Server und C# Anwendungen. Der Knackpunkt hierbei sind unterschiedliche Sprachen auf Server und Client.

Habe ich einen englischen Windows Server mit SQL Server und deutschem Client-Betriebssystem mit der Anwendung, bekomme ich bei meinen "Select" eine Fehlermeldung bei der Abfrage von Daten mit Datumsinhalten.
Ist beides Deutsch, kein Problem, ist beides englisch -> auch kein Problem.

Hat jemand ein "Allerheilmittel" dafür? Ich möchte die Abfragen mit Datum/Zeit sprachenunabhängig schreiben.

Für konstruktive Ideen und evtl. auch Code Snippeds wäre ich dankbar.

Mit besten Grüßen

O
79 Beiträge seit 2011
vor 3 Jahren

Zumindest im Microsoft SQL-Server gibt es die CONVERT-Funktion, mit der man Datumsangaben konvertieren kann. Der Funktion gibt man einen Paramter mit, der das Format beschreibt, in der das Datum angegeben ist - das macht es systemunabhängig.

T
2.219 Beiträge seit 2008
vor 3 Jahren

Mir ist nicht ganz klar wo das Problem liegt?
Wenn du die Daten per select auslesen willst, dann liest man die Daten z.B. per DataReader.
Dieser kümmert sich dann um die Konvertierung der Datenbank Werte zu DateTime.
Und beim speichern nimmt man einen SqlParameter.
Dieser kümmert sich dann umgekehrt um die richtige Speicherung der Werte als DateTime.

Wenn du aber hingehst und die Werte selbst ausliest, machst du dir einen Heiden Aufwand da du ggf. jede von dir unterstützte sprache supporten müsstest.
Den Aufwand kann man sich hier aber sparen, dafür hat Microsoft selbst gesorgt.

SqlDataReader.GetDateTime

Nachtrag:
Wie sieht den dein bisheriger Ansatz aus?
Also wie sieht der Code aus mit dem du die Daten ausliest?

Nachtrag 2:
Sowie ich dich aktuell verstehe greifen deine Clients auch direkt auf die DB zu.
Ist eine ganz schlechte Idee!
Ein Client sollte über einen Webservice bzw. über eine WebAPI mit dem Server kommunizieren.
Die Clients sollte niemals direkt auf die Datenbank Zugriff haben.
Du hast durch solch ein Konzept gar keine Zugangskontrolle und jeder Client schleift dann auch noch die Zugangsdaten der Datenbank mit sich.

Hier muss der Client mit dem Server via Api kommunizieren.
Der Client bekommt dann nur seine lokale Datenbank, die die Objekte im Client Format speichert.
Dadurch hast du auch eine saubere Trennung zwischen Client und Server aus Architektur sicht und gleichzeitig die Absicherung der Datenbank durch Abschottung von den Clients.
Dies umzubauen würde auch dein ursprüngliches Problem nebenbei erschlagen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

K
kolibri0769 Themenstarter:in
4 Beiträge seit 2020
vor 3 Jahren

@T-Virus,

ist vielleicht etwas komisch geschrieben, aber die Anwendung läuft auf Clients im lokalen Netz und der Zugriff erfolgt über einen ConnectionString auf die DB.
Und ja, ich nutze den DataReader und zum Speichern nehme ich SQLParam...

Klingt jetzt vielleicht etwas "komisch" ist auch nicht böse gemeint, aber ich entwickle Software seit 1997 und das Problem hatte ich seit dem noch nie bzw. eine Formatierung in der Anwendung hat geholfen, was mir aber einfällt: Das Problem habe ich erst seit SQL Server 2019...

Danke auch für den Link, aber genau das ist das, was ich tue 😉

@OlafSt: Das von Dir erwähnte Thema werde ich mal näher beleuchten.

Vielen Dank an Euch beide!

Mit besten Grüßen...

T
2.219 Beiträge seit 2008
vor 3 Jahren

@kolibri0769
Dann zeig mal den Code + die Fehlermeldung.
Eigentlich sollte der DataReader dir das DateTime liefern unabhängig von der Sprache des Client und Servers.
Wäre mir neu, dass diese auch im Einklang sein müssen, damit die Daten richtig geliefert werden.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

F
10.010 Beiträge seit 2004
vor 3 Jahren

Hat jemand ein "Allerheilmittel" dafür? Ich möchte die Abfragen mit Datum/Zeit sprachenunabhängig schreiben.

Und ja, ich nutze den DataReader und zum Speichern nehme ich SQLParam...

Dann solltest Du Dir
[Artikelserie] SQL: Parameter von Befehlen
nochmal anschauen.

SqlParameter benutzt man nicht nur zum Speichern.

87 Beiträge seit 2016
vor 3 Jahren

Hallo,

was kommt denn für eine Fehlermeldung?

Der SELECT an sich dürfte nicht das Problem sein, sondern wie du die Datumswerte weiter verarbeitest/behandelst.

glandorf

K
kolibri0769 Themenstarter:in
4 Beiträge seit 2020
vor 3 Jahren

Hi Glandorf,

die Fehlermeldung ist:

"Conversion failed when converting date and/or time from character string"

Ich poste später mal noch den Code...

@FZelle: Mache ich 😉

Danke an alle schon mal... Melde mich noch mal

Beste Grüße

so, hier noch der Code (Anmerkung dDateFrom und dDateTo sind vom Typ DateTime):


if (dDateFrom != dDateTo)
 {
			sSelString = "SELECT [AGH_ID],[AGH_ORDTYPE],[AGH_AGBNR],[AGH_AGBNAME],[AGH_AKTUELL],[AGH_STATUS],[AGH_VKNETTO],[AGH_LS_STATE],[AGH_RE_STATE],[AGH_PERSONAL] FROM [AGBT_Header] WHERE [AGH_KDNR]='" + sKDNR + "' AND [AGH_AKTUELL] BETWEEN '" + dDateFrom + "' AND '" + dDateTo + "'  order by [AGH_ID] desc ";
		}
		if (_sOrderState != "")
		{
			sSelString = "SELECT [AGH_ID],[AGH_ORDTYPE],[AGH_AGBNR],[AGH_AGBNAME],[AGH_AKTUELL],[AGH_STATUS],[AGH_VKNETTO],[AGH_LS_STATE],[AGH_RE_STATE],[AGH_PERSONAL] FROM [AGBT_Header] WHERE [AGH_KDNR]='" + sKDNR + "' AND [AGH_STATUS]='" + _sOrderState + "' order by [AGH_ID] desc ";
		}

		try
		{
			dbCon = new System.Data.SqlClient.SqlConnection();
			dbCon.ConnectionString = SQLConnection;
			dbConCommand = new System.Data.SqlClient.SqlCommand(sSelString, dbCon);
			dbCon.Open();

			if (dbCon.State == ConnectionState.Closed)
			{
				dbCon.Open();
			}

			dgrd.Rows.Clear();
			dbDatareader = dbConCommand.ExecuteReader();

			while (dbDatareader.Read())
			{
				iOrderType = dbDatareader.GetValue(1);
				if (iOrderType == 0)
				{
					dgrd.Rows.Add(new[] {(dbDatareader.GetValue(0)), "Angebot", (dbDatareader.GetValue(2)), (dbDatareader.GetValue(3)), (dbDatareader.GetDateTime(4)), (dbDatareader.GetValue(5)), (dbDatareader.GetValue(6)), (dbDatareader.GetValue(7)), (dbDatareader.GetValue(8)), (dbDatareader.GetValue(9))});
				}
				else if (iOrderType == 1)
				{
					dgrd.Rows.Add(new[] {(dbDatareader.GetValue(0)), "Auftrag", (dbDatareader.GetValue(2)), (dbDatareader.GetValue(3)), (dbDatareader.GetDateTime(4)), (dbDatareader.GetValue(5)), (dbDatareader.GetValue(6)), (dbDatareader.GetValue(7)), (dbDatareader.GetValue(8)), (dbDatareader.GetValue(9))});
				}
			}
			dbCon.Close();

		}
		catch (Exception ex)
		{
			return;
		}

T
2.219 Beiträge seit 2008
vor 3 Jahren

FZelle lag hier schon richtig, du nutzt keine SqlParameter.
NIE schreibt man irgendwelche Werte direkt in den SQL String, das öffnet SQL Injection Tür und Tor!
Füg deine Werte per Sql Parameter ein, dann hat sich das Thema erledigt.

Das Hauptproblem dürfte dein dDateFrom und dDateTo sein, da diese wegen der aktuellen CultureInfo des Client in einen unbekannten String umgewandelt werden, mit dem der Sql Server wegen seiner eigenen Culture nicht klar kommt.
Genau solche Fälle kann man mit einem SqlParameter abfangen.

Nachtrag:
Warum machst du direkt nach dem öffnen der Verbindung ein if mit Prüfung ob die Verbindung geschlossen ist?
Wenn du die Verbindung direkt öffnest, kann diese nicht geschlossen sein.
Das if kannst du rauswerfen.

Umso mehr ich mir den Code anschaue um so schlimmer wird es.
Warum machst du ein new[]?
Hier solltest du eher ein Konkretes Objekt verwenden.

dgrd richt stark nach DataGrid.
Der Code zum abfragen der Datenbank gehört nicht in die UI.
Hier solltest du dringend deinen Code aufteilen und in einer ordentlichen Architektur über das Drei Schichten Modell umlegen.

Die if Anweisungen in deiner While Schleife mit irgendwelchen GetValue auf den DataReader kann in 2-3 Wochen keiner mehr durchblicken.
Hier solltest du über den DataReader direkt auf die Feldnamen zugreifen und dir möglichst über die Get* Methoden auch gleich die richtigen Typen holen.

Den Code solltest du dringend überarbeiten.
Ich befürchte, das da noch an vielen anderen Stellen solche Obskuren gebilde lauern.
Das wird dir auf Dauer keinen Spaß machen solchen Code zu warten.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

4.931 Beiträge seit 2008
vor 3 Jahren

Daher s.a. [Artikel] Drei-Schichten-Architektur.

Und bei einem DataGrid(View) solltest du die Daten per DataSource (DataBinding) binden (nicht einzeln Zeilen einfügen).

16.807 Beiträge seit 2008
vor 3 Jahren

Und ganz arg wichtig: man verwendet aus C#-sicht DateTimeOffset in der **Datenbank **und nicht DateTime.
DateTime geht die Zeitzone beim Speichern verloren.
datetime vs datetimeoffset in SQL Server: What’s the Difference?

(Keine echte) Alternative: man sorgt manuell dafür, dass in der Datenbank nur UTC gespeichert wird.

K
kolibri0769 Themenstarter:in
4 Beiträge seit 2020
vor 3 Jahren

Hallo an Alle,

vielen Dank für Eure Hinweise und Tipps.

@T-Virus:
Recht hast Du, der Code ist nicht sauber, deshalb habe ich ihn ausgegraben und bin dabei das alles zu überarbeiten 😉

Aktuell überlege ich gerade, ob es nicht mehr Sinn macht mit Linq to SQL alles neu zu machen.
Trotzdem interessant, was ihr hier geschrieben habt.

Also, bleibt alle gesund und danke noch mal.

Mit besten Grüßen.

T
2.219 Beiträge seit 2008
vor 3 Jahren

Anstelle von Linq2Sql würde ich eher Entity Framework oder Dapper verwenden.
Auf der anderen Seite kannst du aber auch den bestehenden Code nehmen, in einen Data Layer auslagern und hast damit schon den meisten Code fertig.
Je nachdem wie komplex der Client insgesamt ist, würde sich ggf. auch lohnen alles neu zu machen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

4.931 Beiträge seit 2008
vor 3 Jahren

"Linq to SQL" ist veraltet, aber generell macht es bei Datenbankprojekten oft Sinn ein ORM zu verwenden.
Aus LINQ to SQL tools in Visual Studio:

In general, new applications should use the Entity Framework when an object-relational mapper layer is required.

=> Entity Framework

Es gibt aber noch andere ORMs, s. z.B. What are the best ORMs for C#?