Share via


Schreiben von generischem Datenzugriffscode in ASP.NET 2.0 und ADO.NET 2.0

 

Dr. Shahram Khosravi
Informationslösungen

April 2005

Gilt für:

   Microsoft ADO.NET 2.0
   Microsoft ASP.NET 2.0
   Microsoft .NET Framework 2.0
   Microsoft Visual Web Developer 2005 Express Edition Beta
   Programmiersprache C#

Zusammenfassung: Verwenden Sie einen schritt-für-Schritt-Ansatz, um zu erfahren, wie Verschiedene ASP.NET 2.0- und ADO.NET 2.0-Tools und -Techniken zum Schreiben von generischem Datenzugriffscode verwendet werden. (18 gedruckte Seiten)

Laden Sie den zugeordneten Beispielcode herunter:GenericDataAccessSample.exe.

Einführung

Die meisten Webanwendungen enthalten Datenzugriffscode für den Zugriff auf den zugrunde liegenden Datenspeicher, um grundlegende Datenvorgänge wie Auswählen, Aktualisieren, Löschen und Einfügen auszuführen. In diesem Artikel wird ein schrittweiser Ansatz verwendet, um zu zeigen, wie Seitenentwickler verschiedene ASP.NET 2.0- und ADO.NET 2.0-Tools und -Techniken nutzen können, um generischen Datenzugriffscode zu schreiben, der für den Zugriff auf verschiedene Arten von Datenspeichern verwendet werden kann. Das Schreiben von generischem Datenzugriffscode ist in datengesteuerten Webanwendungen besonders wichtig, da Daten aus vielen verschiedenen Quellen stammen, darunter Microsoft SQL Server, Oracle, XML-Dokumente, Flatfiles und Webdienste, um nur einige zu nennen.

In diesem Artikel wird eine einfache Webanwendung als Testumgebung für den gesamten hier vorgestellten Code verwendet. Die Anwendung besteht aus zwei Teilen: Der erste Teil ermöglicht es dem Systemadministrator, Newsletter an alle Abonnenten einer Mailingliste zu senden. Der zweite Teil ermöglicht es Benutzern, eine Mailingliste zu abonnieren oder abzubestellen. Der erste Teil des Artikels beginnt mit der Implementierung eines einfachen Datenzugriffscodes (siehe Abbildung 1), um auf Microsoft SQL Server zuzugreifen und die Liste der Abonnenten zu extrahieren. Der Code wird im Laufe des Artikels geändert und generischer gestaltet.

Abbildung 1. Die GetSubscribers-Methode extrahiert die Liste aller Abonnenten.

public IEnumerable GetSubscribers()
{
    SqlConnection con = new SqlConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";

    SqlCommand com = new SqlCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    SqlDataAdapter ad = new SqlDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Da der Datenzugriffscode in Abbildung 1 Code zum Erstellen der ADO.NET-Objekte enthält, z. B. die SqlConnection-, SqlCommand- und SqlDataAdapter-Instanzen . Der Datenzugriffscode kann nicht verwendet werden, um die Liste der Abonnenten aus anderen Datenspeichern wie einer Oracle-Datenbank abzurufen. Seitenentwickler müssen den Datenzugriffscode (mithilfe der GetSubscribers-Methode ) jedes Mal ändern, wenn sie auf einen neuen Datenspeicher zugreifen müssen. Als Nächstes erfahren Sie, wie ADO.NET 2.0 das Anbietermuster verwendet, um Seitenentwicklern beim Schreiben von generischem Datenzugriffscode für den Zugriff auf verschiedene Datenspeichertypen zu helfen.

Anbietermuster in ADO.NET 2.0

Das Standard Problem mit der GetSubscribers-Methode besteht darin, dass sie den Code zum Erstellen der ADO.NET-Objekte enthält. Gemäß dem Anbietermuster muss der Datenzugriffscode die Verantwortung für die Bereitstellung des Codes zum Erstellen der ADO.NET-Objekte an eine andere Klasse delegieren. Ich beziehe mich auf diese Klasse als "Codeanbieterklasse", da sie den Code zum Erstellen der ADO.NET-Objekte bereitstellt. Die Codeanbieterklasse macht Methoden wie CreateConnection, CreateCommand und CreateDataAdapter verfügbar, wobei jede Methode den Code zum Erstellen des entsprechenden ADO.NET-Objekts bereitstellt.

Da die Codeanbieterklasse den tatsächlichen Code enthält, kann dieselbe Klasse nicht für den Zugriff auf verschiedene Datenspeicher verwendet werden. Daher muss der Datenzugriffscode (die GetSubscribers-Methode) geändert und neu konfiguriert werden, um die Verantwortung für die Bereitstellung des Codes an eine neue Codeanbieterklasse zu delegieren, wenn er für den Zugriff auf einen neuen Datenspeicher verwendet wird. Die GetSubscribers-Methode ist weiterhin an den Code gebunden, obwohl sie den Code nicht enthält.

Das Anbietermuster bietet eine Lösung für dieses Problem und besteht aus den folgenden Schritten:

  1. Entwerfen und Implementieren einer abstrakten Basisanbieterklasse.

  2. Leiten Sie alle Codeanbieterklassen von der abstrakten Basisanbieterklasse ab.

  3. Verwenden Sie den Datenzugriffscode (die GetSubscribers-Methode), um die abstrakte Basisklasse anstelle der einzelnen Codeanbieterklassen zu verwenden.

    Die abstrakte Basisklasse delegiert die Verantwortung für die Bereitstellung des Codes zum Erstellen der ADO.NET-Objekte an die entsprechende Unterklasse. Die abstrakte Basisklasse heißt DbProviderFactory. Im Folgenden werden einige der Methoden dieser Klasse vorgestellt:

    public abstract class DbProviderFactory
    {
            public virtual DbConnection CreateConnection();
            public virtual DbCommand CreateCommand();
            public virtual DbDataAdapter CreateDataAdapter();
    }
    

    Jede Unterklasse stellt den Code zum Erstellen der entsprechenden ADO.NET -Objekte für einen bestimmten Datenspeicher bereit. Für instance stellt die SqlClientFactory-Unterklasse den Code zum Erstellen der ADO.NET-Objekte für den Zugriff auf Microsoft SQL Server bereit, wie in Abbildung 2 dargestellt.

    Abbildung 2. Die SqlClientFactory-Klasse und einige ihrer Methoden

    public class SqlClientFactory : DbProviderFactory
    {
            public override DbConnection CreateConnection()
            {
                    return new SqlConnection();
            }
    
           public override DbCommand CreateCommand()
           {
                    return new SqlCommand();
           }
    
           public override DbDataAdapter CreateDataAdapter()
           {
                 return new SqlDataAdapter();
           }
    }
    

Das Anbietermuster ermöglicht es dem Datenzugriffscode, alle Unterklassen gleich zu behandeln, da sie alle Unterklassen derselben Basisklasse sind. Was den Datenzugriffscode betrifft, so sind alle Unterklassen vom Typ DbProviderFactory. Der Datenzugriffscode kann den spezifischen Typ der verwendeten Unterklasse nicht kennen. Dies führt zu einem neuen Problem. Wenn der Datenzugriffscode (die GetSubscribers-Methode) den Typ der Unterklasse nicht kennt, wie kann er dann einen instance der Unterklasse instanziieren?

Die Anbietermusterlösung für dieses Problem besteht aus den folgenden drei Teilen:

  1. Eine eindeutige Zeichenfolge wird verwendet, um jede Unterklasse zu identifizieren. ADO.NET 2.0 verwendet den Namespace der Unterklasse als eindeutige Zeichenfolgen-ID. Für instance identifizieren system.Data.SqlClient und System.Data.OracleClient der eindeutigen Zeichenfolgen-ID die Unterklassen SqlClientFactory bzw. OracleClientFactory.
  2. Eine Textdatei (normalerweise eine XML-Datei) wird verwendet, um Informationen zu allen Unterklassen zu speichern. ADO.NET 2.0 verwendet die machine.config- und web.config-Dateien, um die erforderlichen Informationen zu speichern. Die Informationen zu einer Unterklasse enthalten unter anderem die eindeutige Zeichenfolgen-ID und den Namen des Typs der Unterklasse. Für instance enthalten die Informationen zur SqlClientFactory-Unterklasse die eindeutige Zeichenfolgen-ID System.Data.SqlClient und den Namen des Typs der Unterklasse, z. B. System.Data.SqlClient.SqlClientFactory.
  3. Eine statische Methode wird entworfen und implementiert. Die -Methode kann Teil der abstrakten Basisklasse oder Teil einer separaten Klasse sein. ADO.NET 2.0 verwendet eine separate Klasse namens DbProviderFactories , die die statische GetFactory-Methode verfügbar macht. Die -Methode verwendet die eindeutige Zeichenfolgen-ID der gewünschten Unterklasse als einziges Argument und durchsucht die machine.config-Datei nach einer Unterklasse mit der angegebenen eindeutigen Zeichenfolgen-ID. Die -Methode extrahiert den Namen des Typs der gewünschten Unterklasse und verwendet Reflektion, um dynamisch eine instance der Unterklasse zu erstellen.

Der Datenzugriffscode (getSubscribers-Methode) ruft die statische GetFactory-Methode auf und übergibt die entsprechende eindeutige Zeichenfolgen-ID, um auf die instance der entsprechenden Unterklasse zuzugreifen. Nachdem die GetSubscribers-Methode auf den instance zugegriffen hat, ruft sie die entsprechenden Erstellungsmethoden wie CreateConnection(), CreateCommand() usw. auf, um die entsprechenden ADO.NET Objekte zu instanziieren, wie in Abbildung 3 dargestellt.

Abbildung 3. Die Version der GetSubscribers-Methode, die das neue ADO.NET Anbietermuster verwendet

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";
    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Der Datenzugriffscode (getSubscribers-Methode) delegiert die Verantwortung für die Bereitstellung des Codes zum Erstellen der ADO.NET-Objekte an die Codeanbieterklasse instance, die die GetFactory-Methode instanziiert und zurückgibt. Daher kann derselbe Datenzugriffscode für den Zugriff auf verschiedene Datenspeicher wie Microsoft SQL Server und Oracle verwendet werden.

Der Code zum Erstellen der richtigen ADO.NET-Objekte ist datenspeicherspezifisch. Das Anbietermuster in ADO.NET 2.0 entfernt diese datenspeicherspezifischen Teile aus dem Datenzugriffscode (die GetSubscribers-Methode), um ihn generischer zu machen. Das Anbietermuster entfernt jedoch nicht alle datenspeicherspezifischen Teile. Bei genauerer Betrachtung der GetSubscribers-Methode werden die folgenden datenspeicherspezifischen Teile angezeigt:

  1. Verbindungszeichenfolge
  2. Eindeutige Zeichenfolgen-ID, die die zugrunde liegende Codeanbieterklasse identifiziert
  3. Befehlstext
  4. Befehlstyp

Der Datenzugriffscode ist weiterhin an einen bestimmten Datenspeichertyp gebunden, sofern nicht etwas zu den oben genannten Teilen unternommen wird. Das Anbietermuster in ADO.NET 2.0 hilft bei diesem Problem nicht. ADO.NET 2.0 bietet uns jedoch andere Tools und Techniken, um die ersten beiden datenspeicherspezifischen Teile wie die Verbindungszeichenfolge und die eindeutige Zeichenfolgen-ID aus der GetSubscribers-Methode zu entfernen.

Verbindungszeichenfolgen

Verbindungszeichenfolgen sind einige der wertvollsten Ressourcen in einer Webanwendung. Sie sind so wichtig, dass sie vom .NET Framework 2.0 als "Bürger erster Klasse" behandelt werden. Die web.config-Datei unterstützt jetzt einen neuen Abschnitt mit dem Namen <connectionStrings> , der alle in einer Anwendung verwendeten Verbindungszeichenfolgen enthält. Daher verschieben wir die Verbindungszeichenfolge aus der GetSubscribers-Methode in diesen Abschnitt:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
          <add 
            name="MySqlConnectionString" 
            connectionString="Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
      providerName="System.Data.SqlClient"/>
    </connectionStrings>
</configuration>

Das <Add-Unterelement> des <connectionStrings-Elements> macht die folgenden drei wichtigen Attribute verfügbar:

  • Name– Der Anzeigename der Verbindungszeichenfolge
  • connectionString – Die tatsächliche Verbindungszeichenfolge
  • providerName– Die eindeutige Zeichenfolgen-ID oder invariante Der Codeanbieterklasse

NET Framework 2.0 stellt den Datenzugriffscode (die GetSubscribers-Methode) mit den richtigen Tools bereit, um den Wert der Verbindungszeichenfolge generisch aus der web.config-Datei zu extrahieren, wie im Folgenden beschrieben. Der System.Configuration-Namespace in .NET Framework 2.0 enthält eine neue Klasse mit dem Namen Configuration. Diese Klasse stellt den gesamten Inhalt einer web.config- oder machine.config-Datei dar. Der Datenzugriffscode kann den neuen Operator nicht verwenden, um direkt eine instance dieser Klasse zu erstellen.

Die -Klasse selbst macht eine statische Methode mit dem Namen GetWebConfiguration verfügbar, die den Pfad zur web.config-Datei annimmt und eine instance der Configuration-Klasse zurückgibt, die den gesamten Inhalt der web.config-Datei darstellt:

Configuration configuration = Configuration.GetWebConfiguration("~/");

Eine Klasse, die von der ConfigurationSection-Klasse erbt, stellt jeden Abschnitt der web.config-Datei dar. Der Name der Klasse besteht aus dem Namen des Abschnitts und dem Schlüsselwort (keyword) Abschnitt. Für instance stellt die ConnectionStringsSection-Klasse den Inhalt des <abschnitts connectionStrings> der web.config-Datei dar. Der Datenzugriffscode (die GetSubscribers-Methode) kann den neuen Operator nicht verwenden, um direkt eine instance der ConnectionStringsSection-Klasse zu erstellen. Die Configuration-Klasse macht eine Auflistungseigenschaft namens Sections verfügbar, die alle Objekte enthält, die verschiedene Abschnitte der web.config-Datei darstellen:

ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];

Da Sections eine Auflistung von ConfigurationSection-Objekten ist, muss der Datenzugriffscode die zurückgegebene instance. Nachdem die GetSubscribers-Methode auf das ConnectionStringsSection-Objekt zugreift und es für den Zugriff auf den Verbindungszeichenfolgenwert verwendet:

string connectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;

Abbildung 4 zeigt die neue Version der GetSubscribers-Methode, die den erforderlichen Code enthält, um die Verbindungszeichenfolge auf generische Weise zu extrahieren.

Abbildung 4. Die Version der GetSubscribers-Methode, die die Verbindungszeichenfolge aus der web.config-Datei extrahiert

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();

    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
    con.ConnectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Die GetSubscribers-Methode enthält jetzt die MySqlConnectionString-Zeichenfolge , sodass wir die GetSubscribers-Methode weiterhin ändern müssen, um Unterstützung für einen anderen Datenspeicher wie oracle-Datenbank hinzuzufügen. Es scheint, als wären wir wieder auf dem ersten Platz. Nicht wirklich. Wir haben einige wichtige Vorteile gewonnen, indem wir die Verbindungszeichenfolge aus dem Datenzugriffscode in die web.config-Datei verschieben:

  • Die Verbindungszeichenfolge ist jetzt der Wert des connectionString-Attributs des <add-Unterelements> des connectionStrings-Elements der web.config-Datei, bei der es sich um ein XML-Dokument handelt. Das Tolle an einem XML-Dokument ist, dass wir ein einzelnes Element im Dokument verschlüsseln können. Wir müssen nicht das gesamte Dokument verschlüsseln, wenn wir nur einen kleinen Teil davon schützen müssen. Die .NET Framework 2.0 enthält ein Tool, mit dem wir den <Abschnitt connectionStrings> verschlüsseln können, um unsere wichtigste Ressource, die Verbindungszeichenfolgen, zu schützen. Stellen Sie sich vor, wie viel Schaden ein Hacker unserer wertvollen Datenbank anrichten kann, wenn er seine Hand auf unsere Verbindungszeichenfolgen bekommt. Denken Sie daran, dass Verbindungszeichenfolgen nur ein Hacker benötigt, um auf unsere Datenbank zuzugreifen.

  • Es mag so aussehen, als hätten wir nur die folgende Zeichenfolge ersetzt.

    "Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
    

    mit der neuen Zeichenfolge MySqlConnectionString. Es gibt jedoch einen großen Unterschied. Die frühere Zeichenfolge enthält die SQL Server datenbankspezifischen Informationen, die nicht für eine andere Datenbank wie Oracle gelten, aber die letzte Zeichenfolge ist nur ein Anzeigename.

Der Anzeigename kann jedoch weiterhin Probleme verursachen, da er auf eine bestimmte Verbindungszeichenfolge im <Abschnitt connectionStrings> der web.config-Datei verweist. In unserem Beispiel bezieht es sich auf die Verbindungszeichenfolge, die für den Zugriff auf Microsoft SQL Server verwendet wird. Dies bedeutet, dass die GetSubscribers-Methode (der Datenzugriffscode) geändert werden muss, um einen anderen Anzeigenamen für den Zugriff auf einen anderen Datenspeicher wie Oracle zu verwenden.

Um das Ändern des Datenzugriffscodes zu vermeiden, können wir den Anzeigenamen aus dem Datenzugriffscode in den <Abschnitt appSettings> der web.config-Datei verschieben und den Datenzugriffscode dynamisch in der Runtime wie folgt extrahieren lassen:

string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];

Außerdem wird der Anbietername in den <Abschnitt appSettings> verschoben:

string providerName = ConfigurationSettings.AppSettings["ProviderName"];

Seitenentwickler ändern einfach das Value-Attribut des <Add-Unterelements> des <appSettings-Elements in denselben Datenzugriffscode> , um auf einen anderen Datenspeicher zuzugreifen, ohne änderungen am Datenzugriffscode selbst vorzunehmen.

Abbildung 5 zeigt die Version des Datenzugriffscodes (getSubscribers-Methode), der die letzten Änderungen enthält.

Abbildung 5. Die Version der GetSubscribers-Methode zum Extrahieren des Anbieternamens und des Anzeigenamens der Verbindungszeichenfolge aus der web.config-Datei

public IEnumerable GetSubscribers()
{
    string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];
    string providerName = ConfigurationSettings.AppSettings["ProviderName"];
    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];

    

    DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = section.ConnectionStrings[connectionStringName].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Datenquellen-Steuerelemente

Die neueste Version der GetSubscribers-Methode wie in Abbildung 5 dargestellt ist aufgrund der folgenden Probleme immer noch nicht generisch:

  • Die -Methode enthält datenspeicherspezifische Informationen, d. h. den Befehlstext und den Befehlstyp. Daher müssen Seitenentwickler die Methode noch ändern, bevor sie sie für den Zugriff auf eine andere Datenbank verwenden können.
  • Die -Methode gibt einen instance der DataView-Klasse an ihre Aufrufer zurück, sodass die Methode ihre Aufrufer tabellarische Daten mitfüttert. Wir können uns dies als Vertrag zwischen der GetSubscribers-Methode und ihren Aufrufern vorstellen. Die Aufrufer erwarten, dass die GetSubscribers-Methode den Vertrag unter allen Umständen berücksichtigt, auch wenn der zugrunde liegende Datenspeicher selbst nicht tabellarisch ist. Da die GetSubscribers-Methode ADO.NET Objekte verwendet, um auf den zugrunde liegenden Datenspeicher zuzugreifen, kann sie nicht auf einen hierarchischen Datenspeicher zugreifen, z. B. auf eine XML-Datei, in der Instanzen der Klassen im System.Xml und deren Unternamespaces anstelle von ADO.NET -Objekten verwendet werden müssen.

Das Standard Problem mit dem Datenzugriffscode, der in der GetSubscribers-Methode verwendet wird, besteht darin, dass er direkt den tatsächlichen Code enthält, der die Daten aus dem zugrunde liegenden Datenspeicher extrahiert. Dies ist genau die Art von Problem, für das das .NET Framework 2.0-Anbietermuster speziell entwickelt wurde. Gemäß dem Anbietermuster muss die GetSubscribers-Methode die Verantwortung für die Bereitstellung des Codes für den Zugriff auf den Datenspeicher an eine andere Klasse delegieren. Sie wird als Codeanbieterklasse bezeichnet. Der Typ der Codeanbieterklasse hängt vom Typ des Datenspeichers ab, auf den zugegriffen wird. Diese Codeanbieterklassen werden zusammen als Datenquellensteuerelemente bezeichnet. ASP.NET 2.0 enthält verschiedene Arten von Datenquellensteuerelementen, einschließlich SqlDataSource-, AccessDataSource-, ObjectDataSource-, XmlDataSource- und SiteMapDataSource-Steuerelementen .

Das SqlDataSource-Steuerelement ist speziell für das Aktualisieren, Löschen, Einfügen und Extrahieren von Daten aus relationalen Datenspeichern wie Microsoft SQL Server und Oracle konzipiert. Das AccessDataSource-Steuerelement ist eine Unterklasse des SqlDataSource-Steuerelements, das weiß, wie mit Microsoft Access-Datenbanken gearbeitet wird. Das ObjectDataSource-Steuerelement verwendet hingegen In-Memory-Geschäftsobjekte als Datenspeicher. Das XmlDataSource-Steuerelement wurde speziell für das Extrahieren von Daten aus XML-Dokumenten entwickelt. Das XmlDataSource-Steuerelement bietet jedoch keinen Schreibzugriff auf das zugrunde liegende XML-Dokument.

Jedes Datenquellensteuerelement macht eine oder mehrere Ansichten des zugrunde liegenden Datenspeichers verfügbar. Jede Ansicht ist ein instance einer entsprechenden Klasse. Für instance machen die Steuerelemente SqlDataSource, AccessDataSource und ObjectDataSource Ansichten verfügbar, die Instanzen der SqlDataSourceView-, AccessDataSourceView- und ObjectDataSourceView-Klasse sind. Ansichten blenden den tatsächlichen Typ des zugrunde liegenden Datenspeichers aus, und er verhält sich wie der Typ, den der Datenzugriffscode erwartet. Für instance erwartet die GetSubscribers-Methode einen tabellarischen Datentyp, da sie ihre Clients mit tabellarischen Daten versorgt. Mit den tabellarischen Ansichten kann die GetSubscribers-Methode tabellarische Daten aus dem zugrunde liegenden Datenspeicher extrahieren, auch wenn der Datenspeicher selbst eine hierarchische Datenquelle ist, z. B. ein XML-Dokument. Dadurch kann die GetSubscribers-Methode sowohl tabellarische als auch hierarchische Datenspeicher als tabellarische Datenspeicher behandeln.

Datenquellensteuerelemente können ihren Clients zwei Arten von Ansichten bereitstellen: tabellarisch und hierarchisch. ASP.NET 2.0 enthält zwei Datenquellensteuerelemente, die beide Arten von Ansichten bereitstellen: XmlDataSource und SiteMapDataSource. Die restlichen Datenquellensteuerelemente – SqlDataSource, AccessDataSource und ObjectDataSource – enthalten nur tabellarische Ansichten. Sie können jedoch erweitert werden, um sowohl tabellarische als auch hierarchische Ansichten bereitzustellen.

Tabellarische Datenquellensteuerelemente

Ein tabellarisches Datenquellensteuerelement bewirkt, dass der zugrunde liegende Datenspeicher wie ein tabellarischer Datenspeicher fungiert, unabhängig davon, ob der Datenspeicher tabellarisch ist oder nicht. Ein tabellarischer Datenspeicher besteht aus Tabellen mit Zeilen und Spalten, in denen jede Zeile ein Datenelement darstellt. Der Name einer Tabelle identifiziert und sucht die Tabelle unter anderen Tabellen im tabellarischen Datenspeicher. Eine tabellarische Ansicht verhält sich wie eine Tabelle, was bedeutet, dass Ansichten benannt sind.

Wie bereits erläutert, stellen jede Datenquellensteuerungsklasse und die zugehörige Ansichtsklasse (z. B. die SqlDataSource-Klasse und die zugehörige SqlDataSourceView-Klasse) den tatsächlichen Code zum Aktualisieren, Löschen, Einfügen und Extrahieren von Daten aus dem zugrunde liegenden Datenspeicher bereit. Offensichtlich ist der Code für jedes Datenquellensteuerelement und die zugehörige Ansichtsklasse speziell für die Verwendung mit einem bestimmten Typ von Datenspeicher konzipiert. Daher sind jede Datenquellensteuerungsklasse und die zugehörige Ansichtsklasse datenspeicherspezifisch. Dies stellt ein ernstes Problem für die GetSubscribers-Methode dar, die Datenquellensteuerelemente und deren tabellarische Ansichten für den Zugriff auf den zugrunde liegenden Datenspeicher verwendet, da sie die Methode an einen bestimmten Typ von Datenspeicher bindet, was bedeutet, dass dieselbe Methode nicht für den Zugriff auf verschiedene Typen von Datenspeichern verwendet werden kann.

ASP.NET 2.0 bietet eine Lösung, die das Anbietermuster für Folgende Zwecke verwendet:

  1. Einführung der IDataSource-Schnittstelle und der abstrakten DataSourceView-Klasse
  2. Ableiten aller tabellarischen Datenquellensteuerelemente von der IDataSource-Schnittstelle
  3. Ableiten aller tabellarischen Ansichten von der abstrakten DataSourceView-Klasse

Die IDataSource-Schnittstelle und die abstrakte DataSourceView-Klasse delegieren die Verantwortung für die Bereitstellung des tatsächlichen Codes zum Aktualisieren, Löschen, Einfügen und Extrahieren der Daten aus dem Datenspeicher in die entsprechenden Unterklassen. Der Datenzugriffscode wie die GetSubscribers-Methode muss die Methoden und Eigenschaften der IDataSource-Schnittstelle und der abstrakten DataSourceView-Klasse verwenden. Sie dürfen keine Methode oder Eigenschaft verwenden, die für eine bestimmte Datenquellensteuerelementklasse wie SqlDataSource oder eine bestimmte Datenquellensichtklasse wie SqlDataSourceView spezifisch ist. Das Anbietermuster ermöglicht es dem Datenzugriffscode, alle Datenquellensteuerelemente und ihre jeweiligen Datenquellensichten auf generische Weise zu behandeln. Was den Datenzugriffscode angeht, sind alle Datenquellensteuerelemente vom Typ IDataSource und alle Datenquellensichten vom Typ DataSourceView. Der Datenzugriffscode kann den tatsächlichen Typ des verwendeten Datenquellensteuerelements und des Datenquellensichtobjekts nicht kennen. Dies verursacht ein neues Problem. Wenn der Datenzugriffscode (die GetSubscribers-Methode) den Typ des Datenquellensteuerelements nicht kennt, wie kann es dann eine instance instanziieren?

Wie bereits erläutert, stellt das Anbietermuster eine Lösung bereit, die aus den folgenden Schritten besteht:

  1. Eine eindeutige Zeichenfolgen-ID wird verwendet, um jede Datenquellensteuerungsklasse zu identifizieren.
  2. Eine Textdatei (normalerweise eine XML-Datei) wird verwendet, um Informationen zu allen Datenquellensteuerungsklassen zu speichern.
  3. Es wurde ein Mechanismus entwickelt und implementiert, der die XML-Datei nach einer Unterklasse mit einer angegebenen Zeichenfolgen-ID durchsucht.

Nun sehen wir uns an, wie ASP.NET 2.0 die oben genannten drei Aufgaben des Anbietermusters implementiert. ASP.NET 2.0 leitet alle Datenquellensteuerelemente von der Control-Klasse ab. Warum leiten Datenquellensteuerelemente von der Control-Klasse ab, wenn sie keinen HTML-Markuptext rendern? Sie stammen von der Control-Klasse ab, sodass sie die folgenden drei wichtigen Features erben können:

  1. Sie können deklarativ instanziiert werden.
  2. Sie speichern und stellen ihre Eigenschaftswerte postbacks wieder her.
  3. Sie werden der Steuerelementstruktur der enthaltenden Seite hinzugefügt.

Mit dem ersten Feature können Seitenentwickler Datenquellensteuerelemente deklarativ in ihren jeweiligen ASPX-Dateien instanziieren. Daher fungiert die ASPX-Datei als text- oder XML-Datei, die für den zweiten Schritt des Anbietermusters erforderlich ist. Die ASP.NET 2.0-Steuerelementarchitektur erstellt dynamisch eine instance des deklarierten Datenquellensteuerelements und weist die instance einer Variablen zu, deren Name der Wert der ID-Eigenschaft des deklarierten Datenquellensteuerelements ist. Dadurch werden die oben genannten ersten und dritten Schritte ausgeführt, die für das Anbietermuster erforderlich sind.

Abbildung 6 zeigt die Version der GetSubscribers-Methode, die die Methoden und Eigenschaften der IDataSource-Schnittstelle und der abstrakten DataSourceView-Klasse verwendet, um auf den zugrunde liegenden Datenspeicher zuzugreifen:

Abbildung 6. Die Version der GetSubscribers-Methode, die die Methoden und Eigenschaften der IDataSource-Schnittstelle und der abstrakten DataSourceView-Klasse verwendet.

void GetSubscribers()
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    DataSourceSelectArguments args = new DataSourceSelectArguments();
    if (dv.CanSort)
        args.SortExpression = "Email";
    DataSourceViewSelectCallback callback = new DataSourceViewSelectCallback(SendMail);
    dv.Select(args, callback);
}

Die erste Zeile der GetSubscribers-Methode zeigt deutlich, dass die -Methode das Datenquellensteuerelement als Objekt vom Typ IDataSource behandelt. Die -Methode kümmert sich nicht um den tatsächlichen Typ des Datenquellensteuerelements, z. B. ob es sich um ein SqlDataSource-, AccessDataSource-, ObjectDataSource- oder XmlDataSource-Steuerelement handelt. Dadurch können Seitenentwickler von einer Datenquellensteuerung zu einer anderen wechseln, ohne den Datenzugriffscode ändern zu müssen (die GetSubscribers-Methode). Im nächsten Abschnitt wird dieses wichtige Problem ausführlicher erläutert.

Die GetSubscribers-Methode ruft die GetView-Methode des IDataSource-Objekts auf, um auf das Standardobjekt der tabellarischen Ansicht zuzugreifen. Beachten Sie, dass die GetView-Methode ein Objekt vom Typ DataSourceView zurückgibt. Die GetSubscribers-Methode kümmert sich nicht um den tatsächlichen Typ des Ansichtsobjekts, z. B. ob es sich um ein SqlDataSourceView-, AccessDataSourceView-, ObjectDataSourceView- oder XmlDataSourceView-Objekt handelt.

Als Nächstes erstellt die GetSubscribers-Methode eine instance der DataSourceSelectArguments-Klasse, um zusätzliche Vorgänge anzufordern, z. B. das Einfügen, Paging oder Abrufen der Gesamtzeilenanzahl für die Daten, die der Select-Vorgang zurückgibt. Die -Methode muss zuerst den Wert der CanInsert-, CanPage- oder CanRetrieveTotalRowCount-Eigenschaft der DataSourceView-Klasse überprüfen, um sicherzustellen, dass das Ansichtsobjekt den entsprechenden Vorgang unterstützt, bevor es die Anforderung stellt.

Da der Select-Vorgang asynchron ist, registriert die GetSubscribers-Methode die SendMail-Methode als Rückruf. Die Select-Methode ruft automatisch die SendMail-Methode auf, nachdem sie die Daten abfragt und die Daten als Argument übergibt, wie in Abbildung 7 dargestellt.

Abbildung 7. Die SendMail-Methode listet die Daten auf und extrahiert die erforderlichen Informationen.

void SendMail(IEnumerable data)
{
    string firstName = String.Empty;
    string lastName = String.Empty;
   
    IEnumerator iter = data.GetEnumerator();
    while (iter.MoveNext())
    {
        MailMessage message = new MailMessage();
        message.From = "admin@greatnews.com";
        message.To = DataBinder.Eval(iter.Current, "Email").ToString();
        message.Subject = "NewsLetter";
        firstName = DataBinder.Eval(iter.Current, "FirstName").ToString();
        lastName = DataBinder.Eval(iter.Current, "LastName").ToString();
        string mes = "Dear " + firstName + " " + lastName + ",<br/>";
        mes += MessageBody.Text;
        message.Body = mes;
        message.BodyFormat = MailFormat.Html;
      SmtpMail.SmtpServer = "<myserver>";
      SmtpMail.Send(message);
    }
}

Die SendMail-Methode ruft die GetEnumerator-Methode des Objekts auf, das als erstes Argument für den Zugriff auf das Enumeratorobjekt übergeben wird, und verwendet den Enumerator, um die Daten aufzulisten. Die SendMail-Methode verwendet die Eval-Methode der DataBinder-Klasse , um die E-Mail-Adresse, den Vornamen und den Nachnamen jedes Abonnenten zu extrahieren und den Nachrichtenbrief an jeden zu senden.

Wechseln von einer Datenquellensteuerung zu einer anderen

Wie bereits erläutert, erstellt die ASP.NET-Steuerelementarchitektur dynamisch eine instance des Datenquellensteuerelements, das auf der jeweiligen ASPX-Seite deklariert wird, und weist es der Variablen zu, deren Name der Wert der ID-Eigenschaft des deklarierten Datenquellensteuerelements ist. Eine solche dynamische Instanziierung des deklarierten Datenquellensteuerelements isoliert die GetSubscribers-Methode vom tatsächlichen Typ des Datenquellensteuerelements und ermöglicht der Methode, alle Datenquellensteuerelemente als Objekte des Typs IDataSource zu behandeln. Dadurch können Seitenentwickler von einem Typ der Datenquellensteuerung zu einem anderen wechseln, ohne den Datenzugriffscode (die GetSubscribers-Methode) zu ändern. In diesem Abschnitt wird ein Beispiel für einen solchen Fall vorgestellt.

Angenommen, unsere Webanwendung verwendet die GetSubscribers-Methode in Verbindung mit dem ObjectDataSource-Steuerelement, um die Liste der Abonnenten aus einem relationalen Datenspeicher wie Microsoft SQL Server abzurufen:

<asp:SqlDataSource ID="MySource" Runat="Server"
ConnectionString="<%$ ConnectionStrings:MySqlConnectionString %>"
SelectCommand="Select * From Subscribers" />

Angenommen, unsere Webanwendung funktioniert in einer Umgebung, in der die Abonnentenliste möglicherweise auch aus einem XML-Dokument stammt. Das XML-Dokument kann eine lokale XML-Datei oder eine Remoteressource sein, auf die über die URL zugegriffen wird. Daher muss unsere Anwendung in der Lage sein, die Liste der Abonnenten aus XML-Dokumenten abzurufen. Offensichtlich ist das ObjectDataSource-Steuerelement nicht zum Abrufen tabellarischer Daten aus XML-Dokumenten konzipiert. Dies bedeutet, dass wir ein Datenquellensteuerelement verwenden müssen, das tabellarische Daten aus XML-Dokumenten abrufen kann, z. B. das XmlDataSource-Steuerelement.

Die GetSubscribers-Methode verwendet keine Eigenschaft oder Methode, die für die ObjectDataSource- und ObjectDataSourceView-Klassen spezifisch ist, und verwendet nur die Methoden und Eigenschaften der IDataSource-Schnittstelle und der abstrakten DataSourceView-Klasse, um Datenquellensteuerelemente zu behandeln. Wir können problemlos vom ObjectDataSource-Steuerelement zum XmlDataSource-Steuerelement wechseln und denselben Datenzugriffscode wie die GetSubscribers-Methode verwenden, um die Liste der Abonnenten abzurufen:

<asp:XmlDataSource ID="MySource" Runat="Server"
      DataFile="data.xml" XPath="/Subscribers/Subscriber" />

Der Wert des XPath-Attributs des XmlDataSource-Steuerelements ist auf /Subscribers/Subscriber festgelegt, um alle Abonnenten auszuwählen.

Einfüge- und Löschvorgang

Erinnern Sie sich, dass unsere Webanwendung aus zwei Teilen besteht. Im zweiten Teil der Anwendung können Benutzer eine Mailingliste abonnieren/abmelden. Die Subscribe-Methode wird aufgerufen, wenn auf die Schaltfläche Abonnieren geklickt wird, wie in Abbildung 8 dargestellt.

Abbildung 8. Die Subscribe-Methode wird aufgerufen, wenn auf die Schaltfläche Abonnieren geklickt wird.

void Subscribe(Object sender, EventArgs e)
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    KeyedList values = new KeyedList();
    values.Add("Email", Email.Text);
    values.Add("FirstName", FirstName.Text);
    values.Add("LastName", LastName.Text);
    DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
    if (dv.CanInsert)
        dv.Insert(values, callback);
}

Die erste Zeile der Subscribe-Methode zeigt, dass sich die Methode nicht um den tatsächlichen Typ des Datenquellensteuerelements kümmert. Daher können wir zu einem anderen Datenquellensteuerelement wechseln, um einen neuen Datenspeicher zu unterstützen, ohne den Code in der Subscribe-Methode ändern zu müssen.

Die Methode verwendet eine instance der KeyedList-Klasse, um die E-Mail, den Vornamen und den Nachnamen des Abonnenten zu erfassen. Die KeyedList-Klasse muss nicht verwendet werden. Wir können jede Klasse verwenden, die die IDictionary-Schnittstelle implementiert, einschließlich ArrayList, KeyedList usw.

Die Subscribe-Methode überprüft den Wert der CanInsert-Eigenschaft des Datenquellensichtobjekts, um sicherzustellen, dass das Ansichtsobjekt den Insert-Vorgang unterstützt, bevor es die Insert-Methode aufruft. Die Subscribe-Methode übergibt die KeyedList-instance als erstes Argument an die Insert-Methode.

Die Unsubscribe-Methode funktioniert ähnlich wie die Subscribe-Methode. Der Standard Unterschied besteht darin, dass die Unsubscribe-Methode die Delete-Methode des jeweiligen Ansichtsobjekts aufruft, um das Abonnement aus dem zugrunde liegenden Datenspeicher zu entfernen, wie in Abbildung 9 dargestellt.

Abbildung 9. Die Unsubscribe-Methode wird aufgerufen, wenn auf die Schaltfläche "Abmelden" geklickt wird.

void Unsubscribe(Object sender, EventArgs e)
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    KeyedList keys = new KeyedList();
    keys.Add("Email", Email.Text);
    KeyedList oldValues = new KeyedList();
    oldValues.Add("Email", Email.Text);
    DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
    if (dv.CanDelete)
        dv.Delete(keys, oldValues, callback);
}

Hierarchische Datenquellensteuerelemente

Die GetSubscribers-Methode (der Datenzugriffscode) überträgt tabellarische Daten an die Aufrufer. Es gibt jedoch Zeiten, in denen Datenzugriffscode hierarchische Daten an die Aufrufer zurückgeben muss. In diesem Abschnitt wird die Implementierung der Version der GetSubscribers-Methode vorgestellt, die hierarchische Daten zurückgibt. Wir können uns dies als Vertrag zwischen der Methode und ihren Aufrufern vorstellen. Die Aufrufer erwarten, dass die Methode hierarchische Daten aus hierarchischen und tabellarischen Datenspeichern zurückgibt.

ASP.NET 2.0 verwendet das Anbietermuster, um die GetSubscribers-Methode vom tatsächlichen Typ des zugrunde liegenden Datenspeichers zu isolieren, und stellt die Methode mit den hierarchischen Ansichten des Datenspeichers dar. Dadurch kann die Methode sowohl hierarchische als auch tabellarische Datenspeicher als hierarchische Datenspeicher behandeln.

Jedes hierarchische Datenquellensteuerelement ist speziell für die Arbeit mit einem bestimmten Datenspeicher konzipiert. Da jedoch alle hierarchischen Datenquellensteuerelemente die IHierarchicalDataSource-Schnittstelle implementieren und alle hierarchischen Datenquellensichten von der HierarchicalDataSourceView-Klasse abgeleitet sind, muss sich die GetSubscribers-Methode nicht mit den Besonderheiten der einzelnen Datenquellensteuerelemente befassen und kann alle auf generische Weise behandeln.

IHierarchicalEnumerable GetSubscribers()
{
    IHierarchicalDataSource ds = (IHierarchicalDataSource)MySource;
    HierarchicalDataSourceView dv = ds.GetHierarchicalView("/Subscribers");
    return dv.Select();
}

Die erste Zeile der GetSubscribers-Methode zeigt, dass die Methode das Datenquellensteuerelement als Objekt vom Typ IHierarchicalDataSource behandelt und sich nicht um den tatsächlichen Typ des Datenquellensteuerelements kümmert. Dadurch können Seitenentwickler zu einem neuen hierarchischen Datenquellensteuerelement wechseln, um Unterstützung für einen neuen Datenspeicher hinzuzufügen, ohne den Code in der GetSubscribers-Methode ändern zu müssen.

Die GetSubscribers-Methode ruft dann die GetHierarchicalView-Methode der HierarchicalDataSourceView-Klasse auf, um mit dem angegebenen Pfad auf die hierarchische Sicht zuzugreifen, z. B. "/Subscribers". Beachten Sie, dass die Select-Methode nicht asynchron ist. Die Anwendung übergibt die von der GetSubscribers-Methode zurückgegebenen Daten an die SendMail-Methode (siehe Abbildung 15). Beachten Sie, dass die Daten vom Typ IHierarchicalEnumerable sind.

IHierarchicalEnumerable implementiert IEnumerable, was bedeutet, dass die GetEnumerator-Methode verfügbar gemacht wird. Die SendMail-Methode ruft die GetEnumerator-Methode auf, um auf das jeweilige IEnumerator-Objekt zuzugreifen, das anschließend zum Auflisten der Daten verwendet wird. IHierarchicalEnumerable macht auch eine Methode mit dem Namen GetHierarchyData verfügbar, die das enumerierte Objekt übernimmt und das ihm zugeordnete IHierarchyData-Objekt zurückgibt.

Die IHierarchyData-Schnittstelle macht eine wichtige Eigenschaft namens Item verfügbar, die nichts anderes als das Datenelement ist. Die SendMail-Methode verwendet die Eval-Methode der XPathBinder-Klasse , um XPath-Ausdrücke für das Item-Objekt auszuwerten.

Abbildung 10. Die SendMail-Methode listet die Daten auf, extrahiert die erforderlichen Informationen und sendet den Newsletter an jeden Abonnenten.

void SendMail(IHierarchicalEnumerable data)
{
    string firstName = String.Empty;
    string lastName = String.Empty;

    IEnumerator iter = data.GetEnumerator();
    while (iter.MoveNext())
    {
        IHierarchyData ihdata = data.GetHierarchyData(iter.Current);
        MailMessage message = new MailMessage();
        message.From = "admin@subscribers.com";
        message.To = XPathBinder.Eval(ihdata.Item, "@Email").ToString();
        message.Subject = "NewsLetter";
        firstName = XPathBinder.Eval(ihdata.Item, "@FirstName").ToString();
        lastName = XPathBinder.Eval(ihdata.Item, "@LastName").ToString();
        string mes = "Hi " + firstName + " " + lastName + ",<br/>";
        mes += MessageBody.Text;
        message.Body = mes;
        message.BodyFormat = MailFormat.Html;
        SmtpMail.SmtpServer = "MyServer";
        SmtpMail.Send(message);
    }
}

Zusammenfassung

Mit einem schrittweisen Ansatz, der verschiedene ASP.NET 2.0- und ADO.NET 2.0-Tools und -Techniken zeigt, wie Seitenentwickler generischen Datenzugriffscode schreiben können, der für den Zugriff auf verschiedene Arten von Datenspeichern verwendet werden kann.

Dr. Shahram Khosraviist Senior Software Engineer bei Schlumberger Information Solutions (SIS). Shahram ist spezialisiert auf ASP.NET, XML-Webdienste, .NET-Technologien, XML-Technologien, 3D-Computergrafik, HI/Usability, Entwurfsmuster und die Entwicklung ASP.NET Serversteuerelemente und -komponenten. Er verfügt über mehr als 10 Jahre Erfahrung in der objektorientierten Programmierung. Er verwendet verschiedene Microsoft-Tools und -Technologien wie SQL Server und ADO.NET. Shahram hat Artikel über .NET und ASP.NET Technologien für das asp.netPRO-Magazin geschrieben.