Standardzeichensatz unter .Net auslesen

Forum für Fragen rund um Firebird-Software von Drittanbietern.

Moderator: martin.koeditz

Antworten
Groffy
Beiträge: 40
Registriert: Do 12. Apr 2018, 23:14

Hallo Zusammen,

ich möchte gerne unter .Net mittels FirebirdClient.dll den default Characterset auslesen und habe da einen Effekt den ich mir nicht erklären kann. Hier ersteinmal der Code :

Code: Alles auswählen

 public static FbCharset GetDefaultCharSet(string ConnectionString)
{
   FbCharset charset_default = FbCharset.None;

  using (FbConnection connection = new FbConnection(ConnectionString))
  {
    connection.Open();
    using (FbCommand cmd = new FbCommand("SELECT RDB$CHARACTER_SET_NAME FROM RDB$DATABASE", connection))
    {
      string charset = (string)cmd.ExecuteScalar();
      bool ok = Enum.TryParse(charset.Trim(), out charset_default);            
    }
  }

  return charset_default;
}
 
Ich bekomme auch ein Ergebnis in Form eines String zurück. Z.B "ISO8859_1 ", aber die Umwandlung in den zugehörigen Enum schlägt immer fehl. Ich habe den Code schon in soweit abgeändert, dass ich ein Enum.TryParse genommen habe, um eine Exception zu vermeiden. Wenn ich statt ExecuteScalar mit einem FbDataReader versuche den Wert zu lesen, kommt immer NULL zurück. (Das scheint nicht neu zu sein, siehe auch : https://stackoverflow.com/questions/138 ... racter-set)

Was ist so besonders daran diesen Wert aus der Systemtabelle auszulesen?


Wünsche frohe Ostern, und beste Grüße - Ulrich
bfuerchau
Beiträge: 340
Registriert: Mo 7. Mai 2018, 18:09

Das Problem ist, dass das Charset in der Datenbank grundsätzlich in Großbuchstaben gespeichert ist, der Enum allerdings Casesensitive ist.
Du benötigst also eine korrekt Umwandlung:

var value = Enum.GetValues(typeof(FBCharset)).FirstOrDefault(v => string.Equals(v, charset.Trim(), StringComparison.OrdinalIgnoreCase));

"value" enthält dann den passenden Enum (falls zumindest die Schreibweisen alle vorhanden sind).

Wobei ich mich frage, wofür du das benötigst.
Wenn du Parametermarker mit Parameter-Objekten für Commands verwendest, werden Strings automatisch korrekt gewandelt.
Groffy
Beiträge: 40
Registriert: Do 12. Apr 2018, 23:14

bfuerchau hat geschrieben: Mo 18. Apr 2022, 11:27 Das Problem ist, dass das Charset in der Datenbank grundsätzlich in Großbuchstaben gespeichert ist, der Enum allerdings Casesensitive ist.
Hallo bfuerchau,

besten Dank, hab ich wieder dazu gelernt was das parsen von Enums angeht. Komisch, dass mir das bislang noch nie aufgefallen ist...

Du meintest dann Enum.GetNames() statt Enum.GetValues(), oder? Bei Enum.GetValues bekomme ich ein Array mit FbCharset zurück, und darauf kann ich die Methode FirstOrDefault() nicht anwenden, oder gibts da noch eine Syntaxerweiterung die ich nicht kenne?

Warum ich das benutzen möchte? Ich möchte die FirebirdDbComparer Assembly nutzen und die funktioniert nur bei Datenbanken mit identischem default Charset.

Beste Grüße - Ulrich
Groffy
Beiträge: 40
Registriert: Do 12. Apr 2018, 23:14

bfuerchau hat geschrieben: Mo 18. Apr 2022, 11:27 Wenn du Parametermarker mit Parameter-Objekten für Commands verwendest, werden Strings automatisch korrekt gewandelt.
Nachtrag - kannst Du mir diese Technik kurz erklären?
bfuerchau
Beiträge: 340
Registriert: Mo 7. Mai 2018, 18:09

Ja, GetNames() ist korrekt.
Wie alles in .Net, Strings ist erst mal case sensitive.
Aber es gibt noch eine caseinsensitve form des TryParse:

https://docs.microsoft.com/de-de/dotnet ... m-tryparse(system-type-system-readonlyspan((system-char))-system-boolean-system-object@)

Parameter sind wirklich easy:

Code: Alles auswählen

            FbConnection connection = new FbConnection();
            FbCommand command = connection.CreateCommand();
            command.CommandText = "Select * from myTable where F1 = ? and F2 = ?";

            Dictionary<string, FbParameter> parms = new Dictionary<string, FbParameter>
            {
                { "P1", new FbParameter("P1", FbDbType.VarChar) },
                { "P2", new FbParameter("P2", FbDbType.Decimal) },
            };

            command.Parameters.AddRange(parms.Select(p => p.Value));

            parms["P1"].Value = "ABC";
            parms["P2"].Value = 123.45m;

            DataTable table = new DataTable();
            table.Load(command.ExecuteReader());

Parameter werden im Commandtext als "?" definiert.
Über das Dictionary erstelle ich mir für den leichteren Zugriff die Parameter mit sprechenden Namen, die ggf auch aus anderen Quellen stammen können, z.B. nameof(Personal.Name).

Bis zum Teil AddRange ist die Deklaration abgeschlossen.
Der Rest kann dann beliebig oft wiederholt werden.
Analog kann man auch Update/Delete/Insert kodieren.
"Insert into mytable (F1, F2) values(?, ?)"
"Delete ... where " (wie beim Select)
"update mytable set F1 = ?, F2 = ? where K1=?"
usw.

In den Konzept spielen übrigens DataTable und DBDataAdapter eine sehr große Rolle. Passend zu einem Select auf eine Tabelle mit Primary Key generiert der TableAdapter die passenden Delete/Insert/Update.
TableAdapter und DataTable werden dann verheiratet.

Ein TableAdapter.Update() führt dann alles auf einen Schlag aus.
Die DataTable unterstützt bis zu 16,7 Mio Zeilen.

Jetzt stelle Dir also vor, per FBDataReader füllst du eine DataTable.
Anschließend markierst du die Zeilen als "New".
Dann verbindest du zur 2. DB mit einer identischen Tabelle und verküpfst einen Adapter mit einem generierten Insert mit deiner gerade geladenen Tabelle aus DB1 und führst den Adapter.Update() aus.
Und Schwups werden bis zu 16,7 Mio Zeilen in die 2. DB kopiert.
SQL-technisch benötigst du nur einen "select * from mytable".
Den Rest mach ADO.Net.

https://docs.microsoft.com/de-de/dotnet ... ataadapter

Statt der Factory kannst du direkt die Fb-Versionen verwenden.
Groffy
Beiträge: 40
Registriert: Do 12. Apr 2018, 23:14

Hallo,

vielen Dank für Deine Ausführung. Parametermarker in Form von @ParameterName benutze ich ebenfalls, und ich habe mir eine Klasse, abgeleitet von List<FbParameter>, bei der ich mittels eines Namen Indexers auf die Parameter zugreifen kann. Soweit funktioniert das ähnlich wie mit einem Dictionary. Wenn Du mit ? Parametermarkern arbeitest, musst Du dann nicht bei der Zuweisung der Parameter auf die genaue Reihenfolge achten, oder habe ich noch etwas übersehen? So weit so gut, aber den Teil
Wenn du Parametermarker mit Parameter-Objekten für Commands verwendest, werden Strings automatisch korrekt gewandelt.
verstehe ich immer noch nicht so recht.

Die automatische Generierung der Insert/Update/Delete Statements aus dem Select Statement mittels FbDataAdapter ist mir bekannt, benutze ich auch hin und wieder.

Beste Grüße - Ulrich
Zuletzt geändert von Groffy am Mi 20. Apr 2022, 16:08, insgesamt 1-mal geändert.
bfuerchau
Beiträge: 340
Registriert: Mo 7. Mai 2018, 18:09

Ich hatte nicht probiert, ob der FB-Client mit Parameternamen arbeiten kann, der SQL-Server z.B. kann das.
Der FB-Client arbeitet intern leider auch mit einer Liste.
Wenn man also sehr häufig mit myCommand.Parameters["@Name"] zugreift, weil man ggf. mehrere 100 Parameter hat, wird halt die Liste immer wieder durchsuchst. Somit werden ca. 100*100/2 Zugriffe zum Finden gemacht.
Da ist das Dictionary einfach schneller.
Klar, wenn ein neuerer FB-Client Namen kann, kann man das tun.
Allerdings habe ich einen für mich optimierten Client, dessen Reader z.B. ca. 20% schneller liest weil sich zu viel mit anderen Dingen im Code aufgehalten wird. Und der kann noch keine Namen, daher das "?".
Das Abzählen ist da natürlich erforderlich. Da ich aber mit vielen verschiedenen DB's arbeite und das nicht alle können, bin ich's gewohnt.

Was die Umwandlung angeht, so definierst du ja im Parameter nur den Typ char/varchar ohne Zeichensatz. Dem Parameter wird ein String (Unicode) zugewiesen, der wird dann im Ziel in den Zeichensatz umgewandelt.
Beim Lesen aus dem Reader erhält man ebenso wieder Strings, also Unicode.

Per Definition werden bei der Umwandlung von Unicode in einen inkompatiblen Zeichensatz unbekannte Zeichen mit "?" ersetzt.
Spätestens dann sollte einem das aber auch auffallen :ugeek:.
Antworten