DOTnet-Bremen
LogIn:  Passwort:    
Sonntag, 5. September 2010
DOTnet-News
Über uns
LogIn
Registrieren
Termine
Locations
Tipps & Tricks
Usergroup-Bilder
Kontakt
© 2010 by MelNet
sponsored by
IFW-Logo
powered by
Melchers-Logo


supported by
INETA-Logo
Tipps & Tricks

Zurück zur Übersicht Neuen Artikel erstellen Artikel suchen  

Datenbank unabhängiges Programmieren unter ADO.NET
Autor: Michael Jacobsen
Erstelldatum: 05.01.2006
Letzte Änderung: 05.01.2006
Seiten-Aufrufe: 4966

Der Normalfall: Die Verwendung Provider-spezifischer ADO.NET-Klassen

Zu Beginn ein recht einfaches Code-Beispiel, das die Verwendung eines OleDb-Providers demonstriert.

OleDbConnection cn = null; 
try  {
  // Verbindung zu DB herstellen
  string strProvider = @"Provider=Microsoft.Jet.OLEDB.4.0; Data Source=..\Test.mdb);
  cn = new OleDbConnection(strProvider);
  cn.Open();

  // Datensätze durchlaufen
  OleDbCommand cmd = cn.CreateCommand();
  cmd.CommandText = "SELECT * FROM Kunden;";
  OleDbDataReader dr = cmd.ExecuteReader();
  while(dr.Read())
  {
    Console.WriteLine( "{0}, {1}", dr["Vorname"].ToString() , 
                                                   dr["Name"].ToString() , );
  }		
}
catch(Exception ex)  
{
  Console.WriteLine(ex.Message);		
}
finally
{
  con.Close(); // Verbindung wieder schliessen
}

Nun, ich weiss, das Ganze ist für Euch recht trivial und soll nur als Einstieg in den Artikel dienen. Aber was ist der Nachteil an der Sache? Nachteilig daran ist einfach, dass man sich hier auf das verwendete Datenbanksystem - hier Access - festgelegt hat. Ich kann den Programmcode nicht mehr verwenden, wenn ich später das Datenbanksystem wechsele. Im folgenden nun der Lösungsansatz, wie unter .NET dennoch eine Datenbank unabhängige Programmierung realisiert werden kann.

Der kleinste gemeinsame Nenner

Als Lösungsansatz zu obigem Problem machen wir uns die Tatsache zu nutze, dass sämtliche ADO.NET-Klassen generische Schnittstellen implementieren. Die Klassen OleDbConnection, SqlConnection, OdbcConnection, etc. implementieren alle die Schnittstelle IDbConnection. Genauso verhält es sich mit den Klassen OleDbDataAdapter, SqlDataAdapter, OdbcDataAdapter: sie realisieren die Implementation der Schnittstelle IDbDataAdapter usw. Diese generischen, also vom konkreten Typ des Datenbanksystems unabhängige Schnittstellen bilden den kleinsten gemeinsamen Nenner der jeweiligen ADO.NET-Klasse.

Folgende Zuweisungen sind daher möglich:

IDbConnection con; // Variable vom gemeinsamen Typ der generischen Schnittstelle
con = new OleDbConnection();
//...
con = new SqlConnection();

Die Typen OleDbConnection, SqlConnection, etc. sind zuweisungskompatibel zum Typ der Schnittstelle IDbConnection.

Aber ist das Ganze eigentlich wirklich Datenbank unabhängig? Ich benutze ja letztlich doch die Provider-spezifischen Objekte wie OleDbConnection. Welche Lösung gibt es, diese Objekte ganz aus dem Quelltext zu verbannen?



Objekte aus der Fabrik

Um dieses Ziel zu verwirklichen, entwickeln wir eine Klasse, die uns auf Anforderung die gewünschten ADO.NET-Objekte zurückliefert. Eine Art Fabrik-Klasse in Anlehnung an das Factory Pattern. Folgende Code-Sequenz soll damit möglich sein:

using Jakko.Databases; // Namespace referenzieren, der diese Spezialklasse enthält

//...

IDbConnection cn = null; 
try  {
  // Fabrik-Objekt erzeugen 
  ProviderFactory factory = new ProviderFactory();

  // Connection-Objekt von Fabrik anfordern und Verbindung herstellen
  cn = factory.GetConnection();
  cn.Open();

  // Datensätze durchlaufen
  IDbCommand cmd = cn.CreateCommand();
  cmd.CommandText = "SELECT * FROM Kunden;";
  IDataReader dr = cmd.ExecuteReader();
  while(dr.Read())
  {
    Console.WriteLine( "{0}, {1}", dr["Vorname"].ToString() , 
                                                   dr["Name"].ToString() , );
  }		
}
catch(Exception ex)  
{
  Console.WriteLine(ex.Message);  // Oops, da is' was schief gelaufen		
}
finally
{
  con.Close(); // Verbindung wieder schliessen
}

Ja, das sieht völlig losgelöst aus von jeglichem Datenbanksystem. Ich kann anhand des Programmcodes keinerlei Rückschlüsse mehr ziehen über das verwendete Datenbanksystem. Aber irgendwo muss die Klasse ja die Information über das verwendete Datenbanksystem hernehmen. Entmystifizieren wir die Klasse 'ProviderFactory' also und erforschen ihr Innenleben.

Fabrikbesichtigung

Als erstes der komplette Code der Klasse 'ProviderFactory':

using System;
using System.Configuration;
using System.Data;
using System.Data.OleDb;
using System.Data.Odbc;
using ByteFX.Data.MySqlClient;
using System.Data.SqlClient;

namespace Jakko.Databases
{
  
  // Aufzählungstyp für die verschiedenen
  // Datenbanktypen
  public enum ProviderType
  {
    MySQL = 1,
    OleDb = 2,
    MSSQL = 3,
    ODBC = 4
  };
  
  // Fabrik-Klasse für ADO.NET-Objekte
  class ProviderFactory
  {
    public class ProviderFactory
    {
      // 
      // Private Daten
      //
      private ProviderType providerType;
      private string strConnection;

      //
      // Konstruktoren
      //
      public ProviderFactory()
      { 
        // Informationen für Datenbankzugriff aus Konfigurations-
        // datei lesen
        string strProviderType = ConfigurationSettings.AppSettings["ProviderType"];
        this.providerType = 
	  (ProviderType) Enum.Parse( typeof(ProviderType), strProviderType, true) ;
          this.strConnection =
          ConfigurationSettings.AppSettings["Connection"];
      }
      
      public ProviderFactory (ProviderType type, string strConnection)
      {
        // Private Datenfelder mit Werten aus 
	// Argumenten besetzen
	this.providerType = type;
	this.strConnection = strConnection;				
      }

      //
      // Methoden
      //
      public IDbConnection GetConnection()
      {
        IDbConnection con;
	switch(providerType)
	{
	  case ProviderType.OleDb: 
		con = new OleDbConnection(this.strConnection);
		break;
	  case ProviderType.ODBC: 
		con = new OdbcConnection(this.strConnection);
		break;
	  case ProviderType.MySQL:
		con = new MySqlConnection(this.strConnection);
		break;
	  case ProviderType.MSSQL:
		con = new SqlConnection(this.strConnection);
	        break;	
	   default:
		throw new Exception ("Unbekannter Provider");
	}
	return con;			
      }// GetConnection()

     // weitere Methoden nach dem selben Scheme
     // GetDataAdapter() etc.

    }//class

}// namespace

Der Konstruktor

Der parameterlose Standardkonstruktor, der in obigem Beispiel verwendet wird, liest die Informationen über das zu verwendende Datenbanksystem aus der Konfigurationsdatei der Anwendung. Das ist die app.exe.config bei Windows-Forms- bzw. web.config bei ASP.NET-Anwendungen. Um aus einer Zeichenkette ein Enum zu machen, findet die statische Methode Enum.Parse Anwendung.

Daneben gibt es noch den parametrisierten Konstruktor, dem man die Werte als Argumente übergibt, falls man nicht mit Konfigurationsdateien arbeitet.

Die Methode( n )

Die einzige Methode der Klasse 'ProviderFactory' ist hier 'GetConnection( )'. Sie arbeitet intern - wie man sieht - mit einer switch-Anweisung. Diese wird ausgewertet und auf Basis der privaten Objektvariable providerType das gewünschte ADO.NET-Objekt zurückgeliefert. Nach dem gleichen Schema kann man die Klasse noch um weitere Methoden wie z.B. 'GetDataAdapter', 'GetCommand', etc. erweitern.

Und sonst...

...sind ganz oben natürlich noch u.a. die Namensräume der ADO.NET-Provider zu referenzieren, die die Klasse 'ProviderFactory' verarbeiten kann.

Fazit

Wenn man mit der Funktionalität auskommt, mit der die generischen Schnittstellen der ADO.NET-Provider daher kommen, ist Datenbank unabhängiges Programmieren unter .NET durchaus möglich.

nach oben  nach oben