Freigeben über


Erstellen einer Datenzugriffsschicht (C#)

von Scott Mitchell

PDF herunterladen

In diesem Lernprogramm beginnen wir von Anfang an und erstellen die Data Access Layer (DAL) mit typierten DataSets, um auf die Informationen in einer Datenbank zuzugreifen.

Einführung

Als Webentwickler dreht sich unser Leben um das Arbeiten mit Daten. Wir erstellen Datenbanken zum Speichern der Daten, des Codes zum Abrufen und Ändern der Daten sowie webseiten, um sie zu sammeln und zusammenzufassen. Dies ist das erste Lernprogramm in einer langen Reihe, die Techniken für die Implementierung dieser allgemeinen Muster in ASP.NET 2.0 untersucht. Wir beginnen mit dem Erstellen einer Softwarearchitektur , die aus einer Data Access Layer (DAL) mit typierten DataSets, einer Geschäftslogikebene (Business Logic Layer, BLL) besteht, die benutzerdefinierte Geschäftsregeln erzwingt, und einer Präsentationsebene, die aus ASP.NET Seiten besteht, die ein gemeinsames Seitenlayout aufweisen. Sobald diese Back-End-Grundlagen festgelegt wurden, werden wir in die Berichterstellung übergehen und zeigen, wie Daten aus einer Webanwendung angezeigt, zusammengefasst, gesammelt und überprüft werden. Diese Lernprogramme sind darauf ausgerichtet, präzise zu sein und schrittweise Anleitungen mit vielen Screenshots bereitzustellen, um Sie visuell durch den Prozess zu führen. Jedes Lernprogramm ist in C#- und Visual Basic-Versionen verfügbar und enthält einen Download des vollständigen verwendeten Codes. (Dieses erste Lernprogramm ist ziemlich langwierig, aber der Rest wird in viel mehr digestiblen Blöcken dargestellt.)

Für diese Lernprogramme verwenden wir eine Microsoft SQL Server 2005 Express Edition-Version der Northwind-Datenbank, die im App_Data Verzeichnis platziert ist. Zusätzlich zur Datenbankdatei enthält der ordner App_Data auch die SQL-Skripts zum Erstellen der Datenbank, falls Sie eine andere Datenbankversion verwenden möchten. Wenn Sie eine andere SQL Server-Version der Northwind-Datenbank verwenden, müssen Sie die EINSTELLUNG NORTHWNDConnectionString in der Datei "Web.config" der Anwendung aktualisieren. Die Webanwendung wurde mit Visual Studio 2005 Professional Edition als dateisystembasiertes Websiteprojekt erstellt. Alle Lernprogramme funktionieren jedoch genauso gut mit der kostenlosen Version von Visual Studio 2005, Visual Web Developer.

In diesem Lernprogramm beginnen wir von Anfang an und erstellen die Data Access Layer (DAL), gefolgt von der Erstellung der Business Logic Layer (BLL) im zweiten Lernprogramm und arbeiten an Seitenlayout und Navigation im dritten. Die Lernprogramme nach dem dritten werden auf dem Fundament aufbauen, das in den ersten drei gelegt wurde. Wir haben viel zu behandeln in diesem ersten Lernprogramm, feuern Sie Also Visual Studio aus, und lassen Sie uns loslegen!

Schritt 1: Erstellen eines Webprojekts und Herstellen einer Verbindung mit der Datenbank

Bevor wir unsere Data Access Layer (DAL) erstellen können, müssen wir zuerst eine Website erstellen und unsere Datenbank einrichten. Erstellen Sie zunächst eine neue dateisystembasierte ASP.NET Website. Wechseln Sie dazu zum Menü "Datei", und wählen Sie "Neue Website" aus, und zeigen Sie das Dialogfeld "Neue Website" an. Wählen Sie die ASP.NET Websitevorlage aus, legen Sie die Dropdownliste "Speicherort" auf "Dateisystem" fest, wählen Sie einen Ordner aus, in dem die Website platziert werden soll, und legen Sie die Sprache auf C# fest.

Erstellen einer neuen dateisystembasierten Website

Abbildung 1: Erstellen einer neuen dateisystembasierten Website (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Dadurch wird eine neue Website mit einer Default.aspx ASP.NET Seite und einem App_Data Ordner erstellt.

Nachdem die Website erstellt wurde, besteht der nächste Schritt darin, einen Verweis auf die Datenbank im Server-Explorer von Visual Studio hinzuzufügen. Durch Hinzufügen einer Datenbank zum Server-Explorer können Sie Tabellen, gespeicherte Prozeduren, Ansichten usw. aus Visual Studio hinzufügen. Sie können tabellendaten auch anzeigen oder eigene Abfragen entweder manuell oder grafisch über den Abfrage-Generator erstellen. Darüber hinaus müssen wir beim Erstellen der typierten DataSets für die DAL Visual Studio auf die Datenbank verweisen, aus der die typierten DataSets erstellt werden sollen. Während wir diese Verbindungsinformationen zu diesem Zeitpunkt bereitstellen können, füllt Visual Studio automatisch eine Dropdownliste der Datenbanken auf, die bereits im Server-Explorer registriert sind.

Die Schritte zum Hinzufügen der Northwind-Datenbank zum Server-Explorer hängen davon ab, ob Sie die SQL Server 2005 Express Edition-Datenbank im ordner App_Data verwenden möchten oder ob Sie stattdessen über ein Microsoft SQL Server 2000- oder 2005-Datenbankserversetup verfügen, das Sie verwenden möchten.

Verwenden einer Datenbank im ordner "App_Data"

Wenn Sie nicht über einen SQL Server 2000- oder 2005-Datenbankserver verfügen, mit dem eine Verbindung hergestellt werden soll, oder Sie einfach vermeiden möchten, dass Sie die Datenbank einem Datenbankserver hinzufügen müssen, können Sie die SQL Server 2005 Express Edition-Version der Northwind-Datenbank verwenden, die sich im App_Data Ordner der heruntergeladenen Website (NORTHWND) befindet. MDF).

Dem Server-Explorer wird automatisch eine Datenbank hinzugefügt, die sich im Ordner App_Data befindet. Angenommen, Sie haben SQL Server 2005 Express Edition auf Ihrem Computer installiert, sollte ein Knoten namens NORTHWND angezeigt werden. MDF im Server-Explorer, den Sie ihre Tabellen, Ansichten, gespeicherte Prozedur usw. erweitern und erkunden können (siehe Abbildung 2).

Der Ordner App_Data kann auch Microsoft Access -.mdb-Dateien enthalten, die wie ihre SQL Server-Entsprechungen automatisch zum Server-Explorer hinzugefügt werden. Wenn Sie keine der SQL Server-Optionen verwenden möchten, können Sie die Northwind Traders-Datenbank und -Apps immer installieren und in das App_Data Verzeichnis ablegen. Beachten Sie jedoch, dass Access-Datenbanken nicht so funktionsreich wie SQL Server sind und nicht für die Verwendung in Websiteszenarien konzipiert sind. Darüber hinaus werden einige der 35 Lernprogramme bestimmte Features auf Datenbankebene nutzen, die von Access nicht unterstützt werden.

Herstellen einer Verbindung mit der Datenbank in einem Microsoft SQL Server 2000- oder 2005-Datenbankserver

Alternativ können Sie eine Verbindung mit einer Northwind-Datenbank herstellen, die auf einem Datenbankserver installiert ist. Wenn der Datenbankserver die Northwind-Datenbank noch nicht installiert hat, müssen Sie sie zuerst auf dem Datenbankserver hinzufügen, indem Sie das Installationsskript ausführen, das im Download dieses Lernprogramms enthalten ist.

Nachdem Sie die Datenbank installiert haben, wechseln Sie zum Server-Explorer in Visual Studio, klicken Sie mit der rechten Maustaste auf den Knoten "Datenverbindungen", und wählen Sie "Verbindung hinzufügen" aus. Wenn der Server-Explorer nicht angezeigt wird, wechseln Sie zum Ansichts-/Server-Explorer, oder drücken Sie STRG+ALT+S. Dadurch wird das Dialogfeld "Verbindung hinzufügen" angezeigt, in dem Sie den Server angeben können, mit dem eine Verbindung hergestellt werden soll, mit den Authentifizierungsinformationen und dem Datenbanknamen. Nachdem Sie die Datenbankverbindungsinformationen erfolgreich konfiguriert und auf die Schaltfläche "OK" geklickt haben, wird die Datenbank unter dem Knoten "Datenverbindungen" als Knoten hinzugefügt. Sie können den Datenbankknoten erweitern, um seine Tabellen, Ansichten, gespeicherten Prozeduren usw. zu erkunden.

Hinzufügen einer Verbindung zur Northwind-Datenbank Ihres Datenbankservers

Abbildung 2: Hinzufügen einer Verbindung zur Northwind-Datenbank Ihres Datenbankservers

Schritt 2: Erstellen der Datenzugriffsebene

Beim Arbeiten mit Daten besteht eine Option darin, die datenspezifische Logik direkt in die Präsentationsebene einzubetten (in einer Webanwendung bilden die ASP.NET Seiten die Präsentationsebene). Dies kann die Form des Schreibens ADO.NET Codes im Codeteil der ASP.NET Seite oder die Verwendung des SqlDataSource-Steuerelements aus dem Markupteil sein. In beiden Fällen koppelt dieser Ansatz die Datenzugriffslogik eng mit der Präsentationsebene. Der empfohlene Ansatz besteht jedoch darin, die Datenzugriffslogik von der Präsentationsebene zu trennen. Diese separate Ebene wird kurz als Datenzugriffsschicht( DAL) bezeichnet und wird in der Regel als separates Klassenbibliotheksprojekt implementiert. Die Vorteile dieser mehrschichtigen Architektur sind gut dokumentiert (siehe Abschnitt "Weitere Lesungen" am Ende dieses Lernprogramms, um Informationen zu diesen Vorteilen zu erhalten) und ist der Ansatz, den wir in dieser Reihe einnehmen werden.

Der gesamte Code, der für die zugrunde liegende Datenquelle spezifisch ist, z. B. das Erstellen einer Verbindung mit der Datenbank, das Ausstellen von SELECT-, INSERT-, UPDATE- und DELETE-Befehlen usw., sollte sich in der DAL befinden. Die Präsentationsschicht sollte keine Verweise auf diesen Datenzugriffscode enthalten, sondern stattdessen Aufrufe an die DAL für alle Datenanforderungen vornehmen. Datenzugriffsebenen enthalten in der Regel Methoden für den Zugriff auf die zugrunde liegenden Datenbankdaten. Die Northwind-Datenbank enthält z. B. Tabellen für Produkte und Kategorien, in denen die Produkte zum Verkauf und die Kategorien erfasst werden, zu denen sie gehören. In unserem DAL werden wir Methoden wie:

  • GetCategories(), die Informationen zu allen Kategorien zurückgeben
  • GetProducts(), die Informationen zu allen Produkten zurückgeben
  • GetProductsByCategoryID(categoryID), die alle Produkte zurückgeben, die zu einer angegebenen Kategorie gehören
  • GetProductByProductID(productID), die Informationen zu einem bestimmten Produkt zurückgibt

Diese Methoden stellen beim Aufrufen eine Verbindung mit der Datenbank her, stellen die entsprechende Abfrage aus und geben die Ergebnisse zurück. Wie wir diese Ergebnisse zurückgeben, ist wichtig. Diese Methoden könnten einfach ein DataSet oder DataReader zurückgeben, das von der Datenbankabfrage aufgefüllt wird, aber im Idealfall sollten diese Ergebnisse mit stark typierten Objekten zurückgegeben werden. Ein stark typiertes Objekt ist ein Objekt, dessen Schema zur Kompilierungszeit starr definiert ist, während das Gegenteil, ein lose typiertes Objekt, ein Schema ist, dessen Schema erst zur Laufzeit bekannt ist.

Beispielsweise sind DataReader und DataSet (standardmäßig) lose typierte Objekte, da ihr Schema durch die spalten definiert wird, die von der Datenbankabfrage zurückgegeben werden, die zum Auffüllen verwendet wird. Um über eine lose typierte DataTable auf eine bestimmte Spalte zuzugreifen, müssen wir Syntax wie: DataTable verwenden. Rows[index]["columnName"]. Die lose Eingabe der DataTable in diesem Beispiel ist durch die Tatsache dargestellt, dass wir mithilfe einer Zeichenfolge oder eines Ordnungsindex auf den Spaltennamen zugreifen müssen. Eine stark typierte DataTable hingegen wird jede der Spalten als Eigenschaften implementiert, was zu Code führt, der wie folgt aussieht: DataTable. Rows[index].columnName.

Um stark typierte Objekte zurückzugeben, können Entwickler entweder eigene benutzerdefinierte Geschäftsobjekte erstellen oder Typed DataSets verwenden. Ein Geschäftsobjekt wird vom Entwickler als Klasse implementiert, deren Eigenschaften in der Regel die Spalten der zugrunde liegenden Datenbanktabelle widerspiegeln, die das Geschäftsobjekt darstellt. Ein typiertes DataSet ist eine Klasse, die von Visual Studio basierend auf einem Datenbankschema generiert wird und deren Member gemäß diesem Schema stark typiert sind. Das Typed DataSet selbst besteht aus Klassen, die die ADO.NET DataSet-, DataTable- und DataRow-Klassen erweitern. Zusätzlich zu stark typierten DataTables enthalten typierte DataSets jetzt auch TableAdapters, die Klassen mit Methoden zum Auffüllen der DataTables des DataSets und zum Verteilen von Änderungen innerhalb der DataTables zurück an die Datenbank sind.

Hinweis

Weitere Informationen zu den Vor- und Nachteilen der Verwendung von Typed DataSets im Vergleich zu benutzerdefinierten Geschäftsobjekten finden Sie unter Entwerfen von Datenebenenkomponenten und Übergeben von Daten über Ebenen.

Wir verwenden stark typierte DataSets für die Architektur dieser Lernprogramme. Abbildung 3 veranschaulicht den Workflow zwischen den verschiedenen Ebenen einer Anwendung, die Typed DataSets verwendet.

Der gesamte Datenzugriffscode wird an den DAL delegiert.

Abbildung 3: Der gesamte Datenzugriffscode wird an die DAL delegiert (Klicken Sie hier, um das Bild mit voller Größe anzuzeigen)

Erstellen eines typierten DataSet- und Tabellenadapters

Um mit der Erstellung unseres DAL zu beginnen, fügen wir zunächst ein Typiertes DataSet zu unserem Projekt hinzu. Klicken Sie dazu im Projektmappen-Explorer mit der rechten Maustaste auf den Projektknoten, und wählen Sie "Neues Element hinzufügen" aus. Wählen Sie die DataSet-Option aus der Liste der Vorlagen aus, und nennen Sie sie "Northwind.xsd".

Wählen Sie das Hinzufügen eines neuen DataSets zu Ihrem Projekt aus.

Abbildung 4: Auswählen des Hinzufügens eines neuen DataSets zu Ihrem Projekt (Klicken Sie hier, um das Bild mit voller Größe anzuzeigen)

Nachdem Sie auf "Hinzufügen" geklickt haben, wählen Sie "Ja" aus, wenn Sie aufgefordert werden, das DataSet zum ordner "App_Code " hinzuzufügen. Der Designer für das typierte DataSet wird dann angezeigt, und der TableAdapter-Konfigurations-Assistent wird gestartet, sodass Sie Das erste TableAdapter-Objekt dem typierten DataSet hinzufügen können.

Ein Typd DataSet dient als stark typierte Sammlung von Daten; sie besteht aus stark typierten DataTable-Instanzen, von denen jede wiederum aus stark typierten DataRow-Instanzen besteht. Wir erstellen eine stark typierte DataTable für jede zugrunde liegende Datenbanktabelle, mit der wir in dieser Lernprogrammreihe arbeiten müssen. Beginnen wir mit dem Erstellen einer DataTable für die Tabelle "Produkte ".

Beachten Sie, dass stark typierte DataTables keine Informationen zum Zugreifen auf Daten aus der zugrunde liegenden Datenbanktabelle enthalten. Um die Daten zum Auffüllen der DataTable abzurufen, verwenden wir eine TableAdapter-Klasse, die als Datenzugriffsschicht fungiert. Für unsere Products DataTable enthält "TableAdapter" die Methoden "GetProducts()", "GetProductByCategoryID(categoryID)" usw., die von der Präsentationsebene aufgerufen werden. Die Rolle der DataTable besteht darin, als stark typierte Objekte zu dienen, die zum Übergeben von Daten zwischen den Ebenen verwendet werden.

Der Konfigurations-Assistent für TableAdapter beginnt, indem Sie aufgefordert werden, auszuwählen, mit welcher Datenbank sie arbeiten soll. In der Dropdownliste werden diese Datenbanken im Server-Explorer angezeigt. Wenn Sie die Northwind-Datenbank nicht zum Server-Explorer hinzugefügt haben, können Sie zu diesem Zeitpunkt auf die Schaltfläche "Neue Verbindung" klicken.

Wählen Sie in der Dropdownliste die Northwind-Datenbank aus.

Abbildung 5: Auswählen der Northwind-Datenbank aus der Dropdownliste (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Nachdem Sie die Datenbank ausgewählt und auf "Weiter" geklickt haben, werden Sie gefragt, ob Sie die Verbindungszeichenfolge in der Datei "Web.config" speichern möchten. Wenn Sie die Verbindungszeichenfolge speichern, vermeiden Sie, dass sie in den TableAdapter-Klassen hartcodiert wird, was die Dinge vereinfacht, wenn sich die Verbindungszeichenfolge Informationen in Zukunft ändern. Wenn Sie sich dafür entscheiden, die Verbindungszeichenfolge in der Konfigurationsdatei zu speichern, wird sie im <Abschnitt "connectionStrings>" abgelegt, der optional für verbesserte Sicherheit verschlüsselt oder später über die neue ASP.NET 2.0-Eigenschaftenseite im IIS-GUI-Verwaltungstool verschlüsselt werden kann, was für Administratoren besser geeignet ist.

Speichern der Verbindungszeichenfolge in Web.config

Abbildung 6: Speichern der Verbindungszeichenfolge in Web.config (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Als Nächstes müssen wir das Schema für die erste stark typierte DataTable definieren und die erste Methode für unser TableAdapter bereitstellen, die beim Auffüllen des stark typierten DataSets verwendet werden soll. Diese beiden Schritte werden gleichzeitig ausgeführt, indem eine Abfrage erstellt wird, die die Spalten aus der Tabelle zurückgibt, die wir in unserer DataTable widerspiegeln möchten. Am Ende des Assistenten geben wir dieser Abfrage einen Methodennamen. Sobald dies erreicht wurde, kann diese Methode über unsere Präsentationsebene aufgerufen werden. Die Methode führt die definierte Abfrage aus und füllt eine stark typierte DataTable auf.

Um mit der Definition der SQL-Abfrage zu beginnen, müssen wir zunächst angeben, wie das TableAdapter die Abfrage ausgeben soll. Wir können eine Ad-hoc-SQL-Anweisung verwenden, eine neue gespeicherte Prozedur erstellen oder eine vorhandene gespeicherte Prozedur verwenden. Für diese Lernprogramme verwenden wir Ad-hoc-SQL-Anweisungen.

Abfragen der Daten mithilfe einer Ad-hoc-SQL-Anweisung

Abbildung 7: Abfragen der Daten mithilfe einer Ad-hoc-SQL-Anweisung (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

An diesem Punkt können wir die SQL-Abfrage manuell eingeben. Beim Erstellen der ersten Methode im TableAdapter möchten Sie normalerweise die Spalten zurückgeben, die in der entsprechenden DataTable ausgedrückt werden müssen. Dazu können wir eine Abfrage erstellen, die alle Spalten und alle Zeilen aus der Tabelle "Artikel " zurückgibt:

Geben Sie die SQL-Abfrage in das Textfeld ein.

Abbildung 8: Eingeben der SQL-Abfrage in das Textfeld (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Verwenden Sie alternativ den Abfrage-Generator, und erstellen Sie die Abfrage grafisch, wie in Abbildung 9 dargestellt.

Grafisches Erstellen der Abfrage über die Abfrage-Editor

Abbildung 9: Grafisches Erstellen der Abfrage über die Abfrage-Editor (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Klicken Sie nach dem Erstellen der Abfrage, aber bevor Sie auf den nächsten Bildschirm wechseln, auf die Schaltfläche "Erweiterte Optionen". In Websiteprojekten ist "Generierung von Anweisungen zum Einfügen, Aktualisieren und Löschen" standardmäßig die einzige erweiterte Option, die standardmäßig ausgewählt ist. Wenn Sie diesen Assistenten aus einer Klassenbibliothek oder einem Windows-Projekt ausführen, wird auch die Option "Optimistische Parallelität verwenden" ausgewählt. Lassen Sie die Option "Optimistische Parallelität verwenden" vorerst deaktiviert. Wir werden optimistische Parallelität in zukünftigen Lernprogrammen untersuchen.

Wählen Sie nur die Option

Abbildung 10: Auswählen der Option "Einfügen", "Aktualisieren" und "Löschen" (Klicken Sie, um das Bild in voller Größe anzuzeigen)

Klicken Sie nach der Überprüfung der erweiterten Optionen auf "Weiter", um zum endgültigen Bildschirm zu wechseln. Hier werden wir aufgefordert, auszuwählen, welche Methoden dem TableAdapter hinzugefügt werden sollen. Es gibt zwei Muster zum Auffüllen von Daten:

  • Eine DataTable mit diesem Ansatz ausfüllen, wird eine Methode erstellt, die eine DataTable als Parameter verwendet und basierend auf den Ergebnissen der Abfrage auffüllt. Die ADO.NET DataAdapter-Klasse implementiert dieses Muster beispielsweise mit der Fill()- Methode.
  • Gibt eine DataTable mit diesem Ansatz zurück, die die Methode erstellt und füllt die DataTable für Sie und gibt sie als Rückgabewert der Methoden zurück.

Sie können das TableAdapter-Objekt mit einem oder beiden Mustern implementieren lassen. Sie können die hier bereitgestellten Methoden auch umbenennen. Lassen Sie beide Kontrollkästchen aktiviert, obwohl wir in diesen Lernprogrammen nur das letztere Muster verwenden. Außerdem benennen wir die eher generische GetData-Methode in "GetProducts" um.

Wenn aktiviert, erstellt das letzte Kontrollkästchen "GenerateDBDirectMethods"Insert()-, Update()- und Delete()- Methoden für tableAdapter. Wenn Sie diese Option deaktiviert lassen, müssen alle Aktualisierungen über die einzige Update() -Methode von TableAdapter durchgeführt werden, die das typierte DataSet, eine DataTable, eine einzelne DataRow oder ein Array von DataRows verwendet. (Wenn Sie die Option "Anweisungen einfügen, aktualisieren und löschen" aus den erweiterten Eigenschaften in Abbildung 9 deaktiviert haben, hat die Einstellung dieses Kontrollkästchens keine Auswirkung.) Lassen Sie dieses Kontrollkästchen aktiviert.

Ändern des Methodennamens von

Abbildung 11: Ändern des Methodennamens von "GetData " in "GetProducts " (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Schließen Sie den Assistenten ab, indem Sie auf "Fertig stellen" klicken. Nachdem der Assistent geschlossen wurde, werden wir an den DataSet-Designer zurückgegeben, der die soeben erstellte DataTable anzeigt. Sie können die Liste der Spalten in der Products DataTable (ProductID, ProductName usw.) sowie die Methoden der ProductsTableAdapter (Fill() und GetProducts()) anzeigen.

Die Products DataTable und ProductsTableAdapter wurden dem typierten DataSet hinzugefügt.

Abbildung 12: Die Products DataTable und ProductsTableAdapter wurden dem typierten DataSet hinzugefügt (Klicken Sie, um das Bild in voller Größe anzuzeigen)

An diesem Punkt verfügen wir über ein typiertes DataSet mit einer einzelnen DataTable (Northwind.Products) und einer stark typierten DataAdapter-Klasse (NorthwindTableAdapters.ProductsTableAdapter) mit einer GetProducts() -Methode. Diese Objekte können verwendet werden, um auf eine Liste aller Produkte aus Code zuzugreifen, z. B.:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
Northwind.ProductsDataTable products;
products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow productRow in products)
    Response.Write("Product: " + productRow.ProductName + "<br />");

Dieser Code erforderte nicht, dass wir ein Bit von Datenzugriffscode schreiben. Wir mussten keine ADO.NET Klassen instanziieren, wir mussten nicht auf Verbindungszeichenfolge s, SQL-Abfragen oder gespeicherte Prozeduren verweisen. Stattdessen stellt "TableAdapter" den Datenzugriffscode auf niedriger Ebene für uns bereit.

Jedes objekt, das in diesem Beispiel verwendet wird, ist ebenfalls stark typiert, sodass Visual Studio IntelliSense und kompilierte Typüberprüfung bereitstellen kann. Und am besten alle vom TableAdapter zurückgegebenen DataTables können an ASP.NET Datenwebsteuerelemente gebunden werden, z. B. GridView, DetailsView, DropDownList, CheckBoxList und mehrere andere. Das folgende Beispiel veranschaulicht das Binden der DataTable, die von der GetProducts() -Methode an eine GridView in nur drei Codezeilen innerhalb des Page_Load Ereignishandlers zurückgegeben wird.

AllProducts.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AllProducts.aspx.cs"
    Inherits="AllProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>View All Products in a GridView</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            All Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

AllProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class AllProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource = productsAdapter.GetProducts();
        GridView1.DataBind();
    }
}

Die Liste der Produkte wird in einer GridView angezeigt.

Abbildung 13: Die Liste der Produkte wird in einer GridView angezeigt (Klicken Sie, um das Bild in voller Größe anzuzeigen)

Dieses Beispiel erforderte zwar, dass wir drei Codezeilen im Page_Load Ereignishandler der ASP.NET Seite schreiben, in zukünftigen Lernprogrammen wird jedoch untersucht, wie die ObjectDataSource zum deklarativen Abrufen der Daten aus dem DAL verwendet wird. Mit objectDataSource müssen wir keinen Code schreiben und erhalten auch Seiten- und Sortierunterstützung!

Schritt 3: Hinzufügen parametrisierter Methoden zur Datenzugriffsebene

An diesem Punkt hat unsere ProductsTableAdapter-Klasse jedoch eine Methode, GetProducts(), die alle Produkte in der Datenbank zurückgibt. Obwohl die Arbeit mit allen Produkten definitiv nützlich ist, gibt es Zeiten, in denen wir Informationen zu einem bestimmten Produkt oder alle Produkte abrufen möchten, die zu einer bestimmten Kategorie gehören. Um diese Funktionalität zu unserer Datenzugriffsebene hinzuzufügen, können wir dem TableAdapter parametrisierte Methoden hinzufügen.

Fügen wir nun die GetProductsByCategoryID(categoryID)- Methode hinzu. Wenn Sie dem DAL eine neue Methode hinzufügen möchten, kehren Sie zum DataSet-Designer zurück, klicken Sie mit der rechten Maustaste im Abschnitt "ProductsTableAdapter ", und wählen Sie "Abfrage hinzufügen" aus.

Klicken Sie mit der rechten Maustaste auf

Abbildung 14: Klicken Sie mit der rechten Maustaste auf "TableAdapter", und wählen Sie "Abfrage hinzufügen" aus.

Wir werden zuerst gefragt, ob wir mithilfe einer Ad-hoc-SQL-Anweisung oder einer neuen oder vorhandenen gespeicherten Prozedur auf die Datenbank zugreifen möchten. Lassen Sie uns eine Ad-hoc-SQL-Anweisung erneut verwenden. Als Nächstes werden wir gefragt, welche Art von SQL-Abfrage wir verwenden möchten. Da wir alle Produkte zurückgeben möchten, die zu einer angegebenen Kategorie gehören, möchten wir eine SELECT-Anweisung schreiben, die Zeilen zurückgibt.

Auswählen, ob eine SELECT-Anweisung erstellt werden soll, die Zeilen zurückgibt

Abbildung 15: Auswählen der Erstellung einer SELECT-Anweisung , die Zeilen zurückgibt (Klicken Sie, um das Bild in voller Größe anzuzeigen)

Der nächste Schritt besteht darin, die SQL-Abfrage zu definieren, die für den Zugriff auf die Daten verwendet wird. Da wir nur die Produkte zurückgeben möchten, die zu einer bestimmten Kategorie gehören, verwende ich dieselbe SELECT-Anweisung von GetProducts(), aber fügen Sie die folgende WHERE-Klausel hinzu: WHERE CategoryID = @CategoryID. Der @CategoryID-Parameter gibt dem TableAdapter-Assistenten an, dass für die methode, die wir erstellen, ein Eingabeparameter des entsprechenden Typs erforderlich ist (nämlich eine nullable ganze Zahl).

Geben Sie eine Abfrage ein, um nur Produkte in einer angegebenen Kategorie zurückzugeben.

Abbildung 16: Eingeben einer Abfrage, um nur Produkte in einer angegebenen Kategorie zurückzugeben (Klicken, um das Bild in voller Größe anzuzeigen)

Im letzten Schritt können wir auswählen, welche Datenzugriffsmuster verwendet werden sollen, sowie die Namen der generierten Methoden anpassen. Für das Füllmuster ändern wir den Namen in FillByCategoryID und für die Rückgabe eines DataTable-Rückgabemusters (die GetX-Methoden), verwenden wir GetProductsByCategoryID.

Auswählen der Namen für die TableAdapter-Methoden

Abbildung 17: Auswählen der Namen für die TableAdapter-Methoden (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Nach Abschluss des Assistenten enthält der DataSet-Designer die neuen TableAdapter-Methoden.

Die Produkte können jetzt nach Kategorie abgefragt werden

Abbildung 18: Die Produkte können jetzt nach Kategorie abgefragt werden

Nehmen Sie sich einen Moment Zeit, um eine GetProductByProductID(productID) -Methode mit derselben Technik hinzuzufügen.

Diese parametrisierten Abfragen können direkt aus dem DataSet-Designer getestet werden. Klicken Sie mit der rechten Maustaste auf die Methode im TableAdapter, und wählen Sie "Vorschaudaten" aus. Geben Sie als Nächstes die Werte ein, die für die Parameter verwendet werden sollen, und klicken Sie auf "Vorschau".

Diese Produkte, die zur Kategorie

Abbildung 19: Diese Produkte, die zur Kategorie "Getränke" gehören, werden angezeigt (Zum Anzeigen des Bilds mit voller Größe klicken)

Mit der GetProductsByCategoryID(categoryID) -Methode in unserer DAL können wir jetzt eine ASP.NET Seite erstellen, die nur diese Produkte in einer bestimmten Kategorie anzeigt. Das folgende Beispiel zeigt alle Produkte, die sich in der Kategorie "Getränke" befinden, die eine Kategorie-ID von 1 aufweisen.

Beverages.asp

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Beverages.aspx.cs"
    Inherits="Beverages" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>Beverages</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             CssClass="DataWebControlStyle">
               <HeaderStyle CssClass="HeaderStyle" />
               <AlternatingRowStyle CssClass="AlternatingRowStyle" />
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

Beverages.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class Beverages : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ProductsTableAdapter productsAdapter = new
         ProductsTableAdapter();
        GridView1.DataSource =
          productsAdapter.GetProductsByCategoryID(1);
        GridView1.DataBind();
    }
}

Diese Produkte in der Kategorie Getränke werden angezeigt

Abbildung 20: Diese Produkte in der Kategorie "Getränke" werden angezeigt (Zum Anzeigen des Bilds mit voller Größe klicken)

Schritt 4: Einfügen, Aktualisieren und Löschen von Daten

Es gibt zwei Muster, die häufig zum Einfügen, Aktualisieren und Löschen von Daten verwendet werden. Das erste Muster, das ich das direkte Datenbankmuster aufrufe, umfasst das Erstellen von Methoden, die beim Aufrufen einen INSERT-, UPDATE- oder DELETE-Befehl für die Datenbank ausgeben, die auf einem einzelnen Datenbankdatensatz ausgeführt wird. Solche Methoden werden in der Regel in einer Reihe von skalaren Werten (ganze Zahlen, Zeichenfolgen, Booleans, DateTimes usw.) übergeben, die den Werten entsprechen, die eingefügt, aktualisiert oder gelöscht werden sollen. Bei diesem Muster für die Tabelle "Products " würde die Löschmethode z. B. einen ganzzahligen Parameter verwenden, der die ProductID des zu löschenden Datensatzes angibt, während die Einfügemethode eine Zeichenfolge für " ProductName", eine Dezimalzahl für " UnitPrice", eine ganze Zahl für " UnitsOnStock" usw. enthält.

Jede Anforderung zum Einfügen, Aktualisieren und Löschen wird sofort an die Datenbank gesendet.

Abbildung 21: Jede Anforderung zum Einfügen, Aktualisieren und Löschen wird sofort an die Datenbank gesendet (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Das andere Muster, auf das ich als Batchaktualisierungsmuster verweise, besteht darin, ein gesamtes DataSet, DataTable oder eine Sammlung von DataRows in einem Methodenaufruf zu aktualisieren. Mit diesem Muster löscht, fügt ein Entwickler die DataRows in einer DataTable ein und ändert sie und übergibt diese DataRows oder DataTable dann an eine Updatemethode. Diese Methode listet dann die übergebenen DataRows auf, bestimmt, ob sie geändert, hinzugefügt oder gelöscht wurden (über den RowState-Eigenschaftswert von DataRow), und gibt die entsprechende Datenbankanforderung für jeden Datensatz aus.

Alle Änderungen werden mit der Datenbank synchronisiert, wenn die Update-Methode aufgerufen wird.

Abbildung 22: Alle Änderungen werden mit der Datenbank synchronisiert, wenn die Update-Methode aufgerufen wird (Klicken Sie, um das Bild in voller Größe anzuzeigen)

The TableAdapter uses the batch update pattern by default, but also supports the DB direct pattern. Da wir beim Erstellen von TableAdapter die Option "Einfügen-, Update- und Delete-Anweisungen generieren" ausgewählt haben, enthält die ProductsTableAdapter eine Update() -Methode, die das Batchaktualisierungsmuster implementiert. Insbesondere enthält das TableAdapter eine Update() -Methode, die das Typed DataSet, eine stark typierte DataTable oder mindestens eine DataRows-Klasse übergeben werden kann. Wenn Sie das Kontrollkästchen "GenerateDBDirectMethods" beim ersten Erstellen des TableAdapter aktiviert haben, wird das direkte Db-Muster auch über Insert()-, Update()- und Delete()- Methoden implementiert.

Beide Datenänderungsmuster verwenden die InsertCommand-, UpdateCommand- und DeleteCommand-Eigenschaften von TableAdapter, um ihre INSERT-, UPDATE- und DELETE-Befehle für die Datenbank auszulegen. Sie können die Eigenschaften InsertCommand, UpdateCommand und DeleteCommand prüfen und ändern, indem Sie im DataSet-Designer auf "TableAdapter" klicken und dann zum Eigenschaftenfenster wechseln. (Stellen Sie sicher, dass Sie den TableAdapter ausgewählt haben und dass die ProductsTableAdapter-Objekt ist die in der Dropdownliste im Eigenschaftenfenster ausgewählte Objekt.)

TableAdapter verfügt über insertCommand-, UpdateCommand- und DeleteCommand-Eigenschaften

Abbildung 23: The TableAdapter has InsertCommand, UpdateCommand, and DeleteCommand Properties (Click to view full-size image)

Um eine dieser Datenbankbefehlseigenschaften zu untersuchen oder zu ändern, klicken Sie auf die CommandText-Untereigenschaft , die den Abfrage-Generator anzeigt.

Konfigurieren der INSERT-, UPDATE- und DELETE-Anweisungen im Abfrage-Generator

Abbildung 24: Konfigurieren der INSERT-, UPDATE- und DELETE-Anweisungen im Abfrage-Generator (Klicken, um das Bild in voller Größe anzuzeigen)

Das folgende Codebeispiel zeigt, wie Sie das Batchaktualisierungsmuster verwenden, um den Preis aller Produkte zu verdoppeln, die nicht mehr eingestellt sind und 25 Einheiten vorrätig oder weniger aufweisen:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
  new NorthwindTableAdapters.ProductsTableAdapter();
// For each product, double its price if it is not discontinued and
// there are 25 items in stock or less
Northwind.ProductsDataTable products = productsAdapter.GetProducts();
foreach (Northwind.ProductsRow product in products)
   if (!product.Discontinued && product.UnitsInStock <= 25)
      product.UnitPrice *= 2;
// Update the products
productsAdapter.Update(products);

Der folgende Code veranschaulicht, wie Sie das direkte DB-Muster verwenden, um ein bestimmtes Produkt programmgesteuert zu löschen, dann eins zu aktualisieren und dann ein neues hinzuzufügen:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
// Delete the product with ProductID 3
productsAdapter.Delete(3);
// Update Chai (ProductID of 1), setting the UnitsOnOrder to 15
productsAdapter.Update("Chai", 1, 1, "10 boxes x 20 bags",
  18.0m, 39, 15, 10, false, 1);
// Add a new product
productsAdapter.Insert("New Product", 1, 1,
  "12 tins per carton", 14.95m, 15, 0, 10, false);

Erstellen benutzerdefinierter Methoden zum Einfügen, Aktualisieren und Löschen

Die methoden Insert(), Update() und Delete() created by the DB direct method can be a bit cumbersome, insbesondere for tables with many columns. Wenn Sie sich das vorherige Codebeispiel ansehen, ist ohne IntelliSenses Hilfe nicht besonders klar, welche Spalte der Products-Tabelle jedem Eingabeparameter den Methoden Update() und Insert() zugeordnet ist. Es kann vorkommen, dass wir nur eine einzelne Spalte oder zwei aktualisieren möchten oder eine angepasste Insert()-Methode möchten, die möglicherweise den Wert des IDENTITÄTsfelds des neu eingefügten Datensatzes (auto-inkrementieren) zurückgibt.

Um eine solche benutzerdefinierte Methode zu erstellen, kehren Sie zum DataSet-Designer zurück. Klicken Sie mit der rechten Maustaste auf "TableAdapter", und wählen Sie "Abfrage hinzufügen" aus, und kehren Sie zum TableAdapter-Assistenten zurück. Auf dem zweiten Bildschirm können wir den Typ der zu erstellenden Abfrage angeben. Erstellen wir nun eine Methode, die ein neues Produkt hinzufügt und dann den Wert der ProductID des neu hinzugefügten Datensatzes zurückgibt. Daher können Sie eine INSERT-Abfrage erstellen.

Erstellen einer Methode zum Hinzufügen einer neuen Zeile zur Artikeltabelle

Abbildung 25: Erstellen einer Methode zum Hinzufügen einer neuen Zeile zur Tabelle " Produkte " (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Auf dem nächsten Bildschirm wird der CommandText von InsertCommand angezeigt. Erweitern Sie diese Abfrage, indem Sie SELECT SCOPE_IDENTITY() am Ende der Abfrage hinzufügen, wodurch der letzte Identitätswert zurückgegeben wird, der in eine IDENTITY-Spalte im selben Bereich eingefügt wird. (Weitere Informationen zu SCOPE_IDENTITY() finden Sie in der technischen Dokumentation und warum Sie wahrscheinlich SCOPE_IDENTITY() anstelle von @@IDENTITY verwenden möchten.) Stellen Sie sicher, dass Sie die INSERT-Anweisung mit einem Semikolon beenden, bevor Sie die SELECT-Anweisung hinzufügen.

Erweitern der Abfrage, um den SCOPE_IDENTITY()-Wert zurückzugeben

Abbildung 26: Erweitern der Abfrage, um den SCOPE_IDENTITY() Wert zurückzugeben (Klicken Sie, um das Bild in voller Größe anzuzeigen)

Nennen Sie schließlich die neue Methode InsertProduct.

Festlegen des Neuen Methodennamens auf InsertProduct

Abbildung 27: Festlegen des neuen Methodennamens auf InsertProduct (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Wenn Sie zum DataSet-Designer zurückkehren, sehen Sie, dass der ProductsTableAdapter eine neue Methode enthält, InsertProduct. Wenn diese neue Methode keinen Parameter für jede Spalte in der Tabelle "Produkte " enthält, haben Sie wahrscheinlich vergessen, die INSERT-Anweisung mit einem Semikolon zu beenden. Konfigurieren Sie die InsertProduct-Methode , und stellen Sie sicher, dass Sie über ein Semikolon verfügen, das die INSERT - und SELECT-Anweisungen trennt.

Standardmäßig stellen Methoden keine Abfragemethoden ein, d. h., sie geben die Anzahl der betroffenen Zeilen zurück. Wir möchten jedoch, dass die InsertProduct-Methode den von der Abfrage zurückgegebenen Wert zurückgibt, nicht die Anzahl der betroffenen Zeilen. Passen Sie dazu die ExecuteMode-Eigenschaft der InsertProduct-Methode an Scalar an.

Ändern der ExecuteMode-Eigenschaft in Skalar

Abbildung 28: Ändern der ExecuteMode-Eigenschaft in Skalar (Klicken, um das Bild in voller Größe anzuzeigen)

Der folgende Code zeigt diese neue InsertProduct-Methode in Aktion:

NorthwindTableAdapters.ProductsTableAdapter productsAdapter =
    new NorthwindTableAdapters.ProductsTableAdapter();
// Add a new product
int new_productID = Convert.ToInt32(productsAdapter.InsertProduct
    ("New Product", 1, 1, "12 tins per carton", 14.95m, 10, 0, 10, false));
// On second thought, delete the product
productsAdapter.Delete(new_productID);

Schritt 5: Abschließen der Datenzugriffsebene

Beachten Sie, dass die ProductsTableAdapters-Klasse die Werte "CategoryID" und "SupplierID" aus der Tabelle "Artikel" zurückgibt, aber nicht die Spalte "CategoryName" aus der Tabelle "Kategorien" oder "CompanyName" aus der Tabelle "Lieferanten" enthält, obwohl dies wahrscheinlich die Spalten sind, die angezeigt werden sollen, wenn Produktinformationen angezeigt werden. Wir können die erste Methode von TableAdapter, GetProducts(), erweitern, um die Spaltenwerte "CategoryName" und "CompanyName" einzuschließen, wodurch auch die stark typisierte DataTable aktualisiert wird, um diese neuen Spalten einzuschließen.

Dies kann jedoch ein Problem darstellen, da die Methoden des TableAdapters zum Einfügen, Aktualisieren und Löschen von Daten von dieser ursprünglichen Methode basieren. Glücklicherweise sind die automatisch generierten Methoden zum Einfügen, Aktualisieren und Löschen von Unterabfragen in der SELECT-Klausel nicht betroffen. Indem wir unsere Abfragen als Unterabfragen zu Kategorien und Lieferanten anstelle von JOIN-Abfragen hinzufügen, vermeiden wir, diese Methoden zum Ändern von Daten neu zu bearbeiten. Klicken Sie im ProductsTableAdapter mit der rechten Maustaste auf die GetProducts()-Methode, und wählen Sie "Konfigurieren" aus. Passen Sie dann die SELECT-Klausel so an, dass sie wie folgt aussieht:

SELECT     ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM         Products

Aktualisieren der SELECT-Anweisung für die GetProducts() -Methode

Abbildung 29: Aktualisieren der SELECT-Anweisung für die GetProducts() -Methode (Klicken, um das Bild in voller Größe anzuzeigen)

Nach dem Aktualisieren der GetProducts()-Methode zur Verwendung dieser neuen Abfrage enthält die DataTable zwei neue Spalten: CategoryName und SupplierName.

Die Products DataTable enthält zwei neue Spalten.

Abbildung 30: Die DataTable " Products " enthält zwei neue Spalten.

Nehmen Sie sich einen Moment Zeit, um die SELECT-Klausel auch in der GetProductsByCategoryID(categoryID)-Methode zu aktualisieren.

Wenn Sie getProducts() SELECT mithilfe der JOIN-Syntax aktualisieren, kann der DataSet-Designer die Methoden zum Einfügen, Aktualisieren und Löschen von Datenbankdaten nicht mithilfe des direkten DB-Musters automatisch generieren. Stattdessen müssen Sie sie manuell wie bei der InsertProduct-Methode weiter oben in diesem Lernprogramm erstellen. Darüber hinaus müssen Sie die Werte der InsertCommand-, UpdateCommand- und DeleteCommand-Eigenschaft manuell angeben, wenn Sie das Batchaktualisierungsmuster verwenden möchten.

Hinzufügen der verbleibenden TableAdapters

Bisher haben wir nur mit einem einzelnen TableAdapter für eine einzelne Datenbanktabelle gearbeitet. Die Northwind-Datenbank enthält jedoch mehrere verwandte Tabellen, mit denen wir in unserer Webanwendung arbeiten müssen. Ein typiertes DataSet kann mehrere verwandte DataTables enthalten. Um unsere DAL abzuschließen, müssen wir daher DataTables für die anderen Tabellen hinzufügen, die wir in diesen Lernprogrammen verwenden. Um einem typierten DataSet ein neues TableAdapter hinzuzufügen, öffnen Sie den DataSet-Designer, klicken Sie mit der rechten Maustaste in den Designer, und wählen Sie "Hinzufügen/TableAdapter" aus. Dadurch wird eine neue DataTable und TableAdapter erstellt und Sie durch den Assistenten geführt, den wir weiter oben in diesem Lernprogramm untersucht haben.

Nehmen Sie sich einige Minuten Zeit, um die folgenden TableAdapters und Methoden mithilfe der folgenden Abfragen zu erstellen. Beachten Sie, dass die Abfragen im ProductsTableAdapter die Unterabfragen enthalten, um die Kategorie- und Lieferantennamen der einzelnen Produkte zu erfassen. Wenn Sie die folgenden Schritte ausgeführt haben, haben Sie außerdem die Methoden "GetProducts()" und "GetProductsByCategoryID(categoryID)" der ProductsTableAdapter-Klasse hinzugefügt.

  • ProductsTableAdapter

    • GetProducts:

      SELECT     ProductID, ProductName, SupplierID, 
      CategoryID, QuantityPerUnit, UnitPrice, UnitsInStock, 
      UnitsOnOrder, ReorderLevel, Discontinued, 
      (SELECT CategoryName FROM Categories WHERE
      Categories.CategoryID = Products.CategoryID) as 
      CategoryName, (SELECT CompanyName FROM Suppliers
      WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      
    • GetProductsByCategoryID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName,
      (SELECT CompanyName FROM Suppliers WHERE
      Suppliers.SupplierID = Products.SupplierID)
      as SupplierName
      FROM         Products
      WHERE      CategoryID = @CategoryID
      
    • GetProductsBySupplierID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE 
      Suppliers.SupplierID = Products.SupplierID) as SupplierName
      FROM         Products
      WHERE SupplierID = @SupplierID
      
    • GetProductByProductID:

      SELECT     ProductID, ProductName, SupplierID, CategoryID,
      QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
      ReorderLevel, Discontinued, (SELECT CategoryName 
      FROM Categories WHERE Categories.CategoryID = 
      Products.CategoryID) as CategoryName, 
      (SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID) 
      as SupplierName
      FROM         Products
      WHERE ProductID = @ProductID
      
  • CategoriesTableAdapter

    • GetCategories:

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      
    • GetCategoryByCategoryID:

      SELECT     CategoryID, CategoryName, Description
      FROM         Categories
      WHERE CategoryID = @CategoryID
      
  • SuppliersTableAdapter

    • GetSuppliers:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      
    • GetSuppliersByCountry:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE Country = @Country
      
    • GetSupplierBySupplierID:

      SELECT     SupplierID, CompanyName, Address,
      City, Country, Phone
      FROM         Suppliers
      WHERE SupplierID = @SupplierID
      
  • EmployeesTableAdapter

    • GetEmployees:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      
    • GetEmployeesByManager:

      SELECT     EmployeeID, LastName, FirstName, Title, 
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE ReportsTo = @ManagerID
      
    • GetEmployeeByEmployeeID:

      SELECT     EmployeeID, LastName, FirstName, Title,
      HireDate, ReportsTo, Country
      FROM         Employees
      WHERE EmployeeID = @EmployeeID
      

Der DataSet-Designer nach dem Hinzufügen der vier TableAdapters

Abbildung 31: Der DataSet-Designer nach dem Hinzufügen der vier TableAdapters (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)

Hinzufügen von benutzerdefiniertem Code zum DAL

Die "TableAdapters" und "DataTables", die dem typierten DataSet hinzugefügt wurden, werden als XML-Schemadefinitionsdatei (Northwind.xsd) ausgedrückt. Sie können diese Schemainformationen anzeigen, indem Sie im Projektmappen-Explorer mit der rechten Maustaste auf die Datei "Northwind.xsd" klicken und "Code anzeigen" auswählen.

Die XML-Schemadefinitionsdatei (XSD) für das vom Typ Northwinds eingegebene DataSet

Abbildung 32: Die XML-Schemadefinitionsdatei (XSD) für das vom Typ Northwinds eingegebene DataSet (Klicken Sie, um das Bild mit voller Größe anzuzeigen)

Diese Schemainformationen werden zur Entwurfszeit beim Kompilieren oder zur Laufzeit (falls erforderlich) in C#- oder Visual Basic-Code übersetzt, an dem Sie ihn mit dem Debugger durchlaufen können. Um diesen automatisch generierten Code anzuzeigen, wechseln Sie zur Klassenansicht, und führen Sie einen Drilldown zu den TableAdapter- oder Typed DataSet-Klassen durch. Wenn die Kursansicht auf Dem Bildschirm nicht angezeigt wird, wechseln Sie zum Menü "Ansicht", und wählen Sie sie dort aus, oder drücken Sie STRG+UMSCHALT+C. In der Klassenansicht können Sie die Eigenschaften, Methoden und Ereignisse der Typed DataSet- und TableAdapter-Klassen anzeigen. Um den Code für eine bestimmte Methode anzuzeigen, doppelklicken Sie in der Klassenansicht auf den Methodennamen, oder klicken Sie mit der rechten Maustaste darauf, und wählen Sie "Gehe zu Definition" aus.

Überprüfen sie den automatisch generierten Code, indem Sie in der Klassenansicht

Abbildung 33: Überprüfen des automatisch generierten Codes durch Auswählen von "Gehe zu Definition" aus der Klassenansicht

Während automatisch generierter Code ein großartiger Zeitsparmodus sein kann, ist der Code häufig sehr generisch und muss an die individuellen Anforderungen einer Anwendung angepasst werden. Das Risiko, automatisch generierten Code zu erweitern, besteht jedoch darin, dass das Tool, das den Code generiert hat, entscheiden kann, dass es an der Zeit ist, "neu zu generieren" und Ihre Anpassungen zu überschreiben. Mit dem neuen Teilklassenkonzept von .NET 2.0 ist es einfach, eine Klasse über mehrere Dateien hinweg aufzuteilen. Auf diese Weise können wir unsere eigenen Methoden, Eigenschaften und Ereignisse zu den automatisch generierten Klassen hinzufügen, ohne sich Gedanken über das Überschreiben unserer Anpassungen machen zu müssen.

Um zu veranschaulichen, wie die DAL angepasst wird, fügen wir der SuppliersRow-Klasse eine GetProducts()-Methode hinzu. Die SuppliersRow-Klasse stellt einen einzelnen Datensatz in der Tabelle "Lieferanten " dar. Jeder Lieferant kann null bis viele Produkte anbieten, sodass GetProducts() diese Produkte des angegebenen Lieferanten zurückgibt. Erstellen Sie hierzu eine neue Klassendatei im ordner App_Code namens SuppliersRow.cs , und fügen Sie den folgenden Code hinzu:

using System;
using System.Data;
using NorthwindTableAdapters;
public partial class Northwind
{
    public partial class SuppliersRow
    {
        public Northwind.ProductsDataTable GetProducts()
        {
            ProductsTableAdapter productsAdapter =
             new ProductsTableAdapter();
            return
              productsAdapter.GetProductsBySupplierID(this.SupplierID);
        }
    }
}

Diese partielle Klasse weist den Compiler an, der beim Erstellen der Northwind.SuppliersRow-Klasse die soeben definierte GetProducts() -Methode enthält. Wenn Sie Ihr Projekt erstellen und dann zur Klassenansicht zurückkehren, wird "GetProducts() jetzt als Methode von Northwind.SuppliersRow" aufgeführt.

Die GetProducts()-Methode ist jetzt Teil der Northwind.SuppliersRow-Klasse.

Abbildung 34: Die GetProducts()-Methode ist jetzt Teil der Northwind.SuppliersRow-Klasse

Die GetProducts()- Methode kann jetzt verwendet werden, um den Satz von Produkten für einen bestimmten Lieferanten aufzählen zu können, wie der folgende Code zeigt:

NorthwindTableAdapters.SuppliersTableAdapter suppliersAdapter =
    new NorthwindTableAdapters.SuppliersTableAdapter();
// Get all of the suppliers
Northwind.SuppliersDataTable suppliers =
  suppliersAdapter.GetSuppliers();
// Enumerate the suppliers
foreach (Northwind.SuppliersRow supplier in suppliers)
{
    Response.Write("Supplier: " + supplier.CompanyName);
    Response.Write("<ul>");
    // List the products for this supplier
    Northwind.ProductsDataTable products = supplier.GetProducts();
    foreach (Northwind.ProductsRow product in products)
        Response.Write("<li>" + product.ProductName + "</li>");
    Response.Write("</ul><p> </p>");
}

Diese Daten können auch in einer beliebigen ASP angezeigt werden. Websteuerelemente für NET-Daten. Auf der folgenden Seite wird ein GridView-Steuerelement mit zwei Feldern verwendet:

  • Ein BoundField, das den Namen der einzelnen Lieferanten anzeigt, und
  • Ein TemplateField,das ein BulletedList-Steuerelement enthält, das an die Ergebnisse gebunden ist, die von der GetProducts()- Methode für jeden Lieferanten zurückgegeben werden.

In zukünftigen Lernprogrammen wird untersucht, wie solche Masterdetailberichte angezeigt werden. In diesem Beispiel wird nun die Verwendung der benutzerdefinierten Methode veranschaulicht, die der Northwind.SuppliersRow-Klasse hinzugefügt wurde.

SuppliersAndProducts.aspx

<%@ Page Language="C#" CodeFile="SuppliersAndProducts.aspx.cs"
    AutoEventWireup="true" Inherits="SuppliersAndProducts" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
    <link href="Styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <h2>
            Suppliers and Their Products</h2>
        <p>
            <asp:GridView ID="GridView1" runat="server"
             AutoGenerateColumns="False"
             CssClass="DataWebControlStyle">
                <HeaderStyle CssClass="HeaderStyle" />
                <AlternatingRowStyle CssClass="AlternatingRowStyle" />
                <Columns>
                    <asp:BoundField DataField="CompanyName"
                      HeaderText="Supplier" />
                    <asp:TemplateField HeaderText="Products">
                        <ItemTemplate>
                            <asp:BulletedList ID="BulletedList1"
                             runat="server" DataSource="<%# ((Northwind.SuppliersRow) ((System.Data.DataRowView) Container.DataItem).Row).GetProducts() %>"
                                 DataTextField="ProductName">
                            </asp:BulletedList>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
             </p>
    </div>
    </form>
</body>
</html>

SuppliersAndProducts.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
public partial class SuppliersAndProducts : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SuppliersTableAdapter suppliersAdapter = new
          SuppliersTableAdapter();
        GridView1.DataSource = suppliersAdapter.GetSuppliers();
        GridView1.DataBind();
    }
}

Der Firmenname des Lieferanten wird in der linken Spalte aufgeführt, deren Produkte rechts

Abbildung 35: Der Firmenname des Lieferanten ist in der linken Spalte aufgeführt, deren Produkte in der rechten Spalte (Klicken Sie hier, um das Bild mit voller Größe anzuzeigen)

Zusammenfassung

Beim Erstellen einer Webanwendung, die das DAL erstellt, sollte einer ihrer ersten Schritte sein, bevor Sie mit dem Erstellen der Präsentationsebene beginnen. Mit Visual Studio ist das Erstellen eines DAL basierend auf typierten DataSets eine Aufgabe, die in 10 bis 15 Minuten ohne Schreiben einer Codezeile ausgeführt werden kann. Die Lernprogramme, die in Zukunft vorangehen, bauen auf diesem DAL auf. Im nächsten Lernprogramm definieren wir eine Reihe von Geschäftsregeln und erfahren, wie sie in einer separaten Geschäftslogikebene implementiert werden.

Glückliche Programmierung!

Weitere nützliche Informationen

Weitere Informationen zu den in diesem Lernprogramm erläuterten Themen finden Sie in den folgenden Ressourcen:

Videoschulung zu Themen, die in diesem Lernprogramm enthalten sind

Zum Autor

Scott Mitchell, Autor von sieben ASP/ASP.NET Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft Web Technologies zusammen. Scott arbeitet als unabhängiger Berater, Trainer und Schriftsteller. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Stunden. Er kann über mitchell@4GuysFromRolla.com seinen Blog erreicht werden, der unter .http://ScottOnWriting.NET

Besonderer Dank an

Diese Lernprogrammreihe wurde von vielen hilfreichen Prüfern überprüft. Leitende Prüfer für dieses Lernprogramm waren Ron Green, Hilton Giesenow, Dennis Patterson, Liz Shulok, Abel Gomez und Carlos Santos. Möchten Sie meine bevorstehenden MSDN-Artikel überprüfen? Wenn dies der Fall ist, legen Sie mir eine Zeile bei mitchell@4GuysFromRolla.com.