Für Programmers: Firebird und Microsoft SQL Server nutzen

Werbung: Mobile Apps für Datenerfassung gibt es bei AESYSTEME


 

Das ist jetzt eine Betrachtung für C# und DOT NET Anwendungsprogrammierer! Der eine oder andere hat es ja schon ausführlich diskutiert: im Rahmen unserer Produktpflege werden wir die Lagerverwaltung AE WWS Lite (Link hier) zukünftig mit zwei SQL Clients versehewn:

  • Für Microsoft SQL Server (incl der kostenfreien Microsoft SQL Server Express Versionen)
  • als auch für Firebird SQL Server. LINK_HIER.

Technisch gesehen benötigt die Software daher zwei SQL Clients: für Microsoft SQL Server und die Firebird DOT NET Bibliothek, beide jeweils als DLL Datei im Programmverzeichnis.

Im ersten Workaround gehen wir davon aus, dass die Software jeweils nur für einen der Clients compiliert wird. Natürlich wäre es problemlos möglich, beide Clients zum gleichen Zeitpunkt zu integrieren – jedoch würde hierbei das Anwendungsprogramm u.E. nach unnötig aufgebläht werden, so dass wir erst mal mit der Trennung auf Compilerebene arbeiten.

De facto sind bereits heute alle SQL Aktivitäten in einer eigenen SQL Klasse ausgelagert. Das hat den Vorteil dass für die Umstellung nur eine Datei angepasst werden muss.

Datei: SQL Class


Preprocessor Compiler Definition

Am Anfang: Preprocessor Defintion für den Compiler. Via Copy und Paste wird hier  direkt vor dem Compilieren wahlweise Firebird oder MSSQL eingetragen. Im Beispiel aktuell wird der Code auf Firebird compiliert:

// Firebird OR MSSQL
#define Firebird

Weil uns Fragen erreichten:

Exkurs: Was macht der Preprocessor im Compiler

Mit dem Preprocessor kann man den Compiler beim Übersetzen der Anwendung dazu bringen, gewisse Teile des Codes zu ignorieren oder zu verwenden. Erreicht wird dieses in der Regel durch

#if (AUSDRUCK)

#elif (AUSDRUCK)

#endif

Formulierungen. Wenn der Compiler findet dass AUSDRUCK wahr (TRUE) ist, wird der eingeschlossene Code compiliert. Ansonsten ignoriert. Dieses Verhalten ist besonders dann von Bedeutung, wenn man seinen Code für bestimmte Bedingungen übersetzen will, z.B. nur für eine bestimmte Sprache oder wie in diesem Fall nur für einen bestimmten SQL Client – und trotzdem in allen Fällen mit dem gleichen Sourcecode in einem Projekt arbeiten möchte.

Und natürlich kann man den Preprocessor auch durch jederzeit eine if Bedingung im Code ersetzen. Dann werden alle Optionen compiliert und man eröffnet dem Anwender die Möglichkeit, beide Teile zu verwenden. (Hier muss man dann natürlich eine Anwendungssteuerung für den Benutzer programmieren, dass dieser mittels Auswahlfeld zur Laufzeit bestimmen kann, welchen Teil er haben möchte. Der Nachteil dieser Kombination: Da immer alles compiliert wurde, wird das Programm hierdurch größer, Insofern sind

#if (DEUTSCH)

hier nur der deutsche Teil

#endif

und 

if (DEUTSCH == TRUE) { deutscher Teil; }

im Prinzip gleichwertig. Allerdings kann man den #if / #endif Teil dazu verwenden dass der Compiler den Inhalt ignoriert = das Programm kürzer macht. Hier gilt es also zu überlegen, ob dem Anwender alle Bedingungen gleichermaßen zur Verfügung stehen müssen, oder ob man mittels Preprocessor Compilerschalter bestimmten Code einfach ausblendet.

 


SQL DLLs einbinden, für Firebird SQL oder Microsoft SQL

Die Einbindung der verschiedenen DLL für die SQL Clients. Es wird nur derjenige SQL Client eingebunden, der im Compiler aktiviert wurde.

#if (MSSQL)
using System.Data.SqlClient;
#endif

#if (Firebird)
using FirebirdSql.Data.FirebirdClient;
#endif

SQL Datensatz schreiben

Beispiel der Routine die einen Datensatz an den SQL Server schreibt – mit Unterstützung SQL Client Microsoft SQL Server und Firebird:

// Nur aktivieren wenn SQL aktiv!

if (Form1.OpenMode != (int)OpenModeValue.SQL) { return;}

// Das SQL Kommando. In diesem Fall haben wir den Namen der SQL Tabelle als Parameter in Form1 definiert. Schöner, aber für unser Beispiel etwas unübersichtlicher, ist es natürlich den Tabellennamen beim Aufruf der Sub zu übergeben.

string cmdstring = "INSERT INTO " + Form1.SQLTable_Data + " (Idx, ArtNr, ArtText, MaterialGroup) " + "VALUES (@Idx, @ArtNr, @ArtText, @MaterialGroup )";

// Die Befehlsfolge für Microsoft SQL

#if (MSSQL)
SqlConnection connection = new SqlConnection(Form1.SQLConnectionstringNeu);
connection.Open();
SqlCommand command = new SqlCommand(cmdstring, connection);
#endif

// Die Befehlsfolge für Firebird SQL

#if (Firebird)
FirebirdSql.Data.FirebirdClient.FbConnection connection = new FirebirdSql.Data.FirebirdClient.FbConnection(Form1.SQLConnectionstringNeu);
connection.Open();
FirebirdSql.Data.FirebirdClient.FbCommand command = new FirebirdSql.Data.FirebirdClient.FbCommand(cmdstring, connection);
#endif

// Und hier die Befehle für beide SQL Clients: Daten füllen und schreiben. Da die Befehle identisch sind, ist eine Trennung nicht mehr notwendig. Wer mag, kann diese Befehle noch in ein “try … catch (exception)” kapseln und sich so vor ungewollten Ausnahmen schützen.

command.Parameters.AddWithValue("@Idx", myAdv["Idx"]);
command.Parameters.AddWithValue("@ArtNr", myAdv["ArtNr"]);
command.Parameters.AddWithValue("@ArtText", myAdv["ArtText"]);
command.Parameters.AddWithValue("@MaterialGroup", myAdv["MaterialGroup"]);

command.ExecuteNonQuery();

// Auf gar keinen Fall Close vergessen!!! Dateien oder Datenbanken nach Verwendung NICHT zu schließen ist ein Kapitalverbrechen für jeden Programmierer und sollte in Fleisch und Blut übergehen.

connection.Close()

SQL Datensätze lesen

Ähnlich aufgebaut wenn alle Datensätze aus einer Tabelle gelesen werden sollen. Auf die erläuternden Kommentare verzichten wir hier und zeigen gleich die ganze Routine:

public bool SQLReadTable(string mySQLTable, DataTable myDT1)
{
//=======================================================
//Ganze Tabelle lesen. Firebird und MSSQL
//Return: TRUE wenn alles OK, FALSE im Fehlerfall
//=======================================================

//Die Datatable wird vor dem Lesen geloescht
myDT1.Clear();

#if (MSSQL)
SqlDataAdapter locAdapter = new SqlDataAdapter("Select * from " + mySQLTable, Form1.SQLConnectionstringNeu);
#endif
#if (Firebird)
FirebirdSql.Data.FirebirdClient.FbDataAdapter locAdapter = new FirebirdSql.Data.FirebirdClient.FbDataAdapter("Select * from " + mySQLTable, Form1.SQLConnectionstringNeu);
#endif

try
{
locAdapter.Fill(myDT1);
}
catch (Exception ex)
{
//Sollte es zu einer Ausnahme gekommen sein, greifen wir die hier ab!
MessageBox.Show("#1610101100 ERROR.\n\n" + ex.Message, "SQL ERROR: sqlreadtable", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return false;
}

return true;

} //end sub