DevTrain

Autor: Tobi Ulm

Relationale Daten mit ADO .net

Aufgrund häufig gestellter Fragen zu relationalen Daten in verschiedenen Foren zu ASP .net & Co., soll dieser Artikel die Vorgehensweise beschreiben, wie mit ADO .net und relationalen Daten gearbeitet werden sollte. Generell sollten Sie sich davon verabschieden Join Statements abzuschicken und auf diese Art und Weise die benötigten Daten aus der Datenquelle zu bekommen. Das DataSet, bzw. die ADO .net Bibliothek bietet hier an der Stelle eine Menge an Objekten die es uns in Zukunft ermöglichen solche relationalen Daten auf einfache Weise zu empfangen und weiter damit zu arbeiten. Wenn man diesen Designansatz weiter verfolgt, wird ganz schnell klar, dass man sich von den liebgewonnenen Join Statements und dem unweigerlichen Update Problem verabschieden kann. Dieser Artikel soll nicht als ?Musterlösung? gelten, denn wer sich mit dem .net Framework beschäftigt, wird feststellen, dass es mehrere Wege nach ?Rom? gibt, und das ist auch gut so! Für eine Enterprise Anwendung ist dieses Beispiel ebenfalls nicht gedacht, da wir die Daten gleich in der Code Behind Seite an Visuelle Controls binden, bzw. wird ein erfahrener Datenbankdesigner vieles der Daten- oder Business Logik in Form von Stored Procedures und Functions innerhalb der Datenbank erledigen. Jedoch kann man einen Teil dieser Logik in einer DataLayer Komponente verwenden.
In vorhergehenden Artikeln zu ADO .net wurden bereits die elementaren Bestandteile einer Datenbank gebundenen Anwendungen, erörtert. Wir werden uns in diesem Artikel hauptsächlich mit folgenden Klassen und deren Objekten beschäftigen: dem DataSet, dem DataAdapter, dem CommandBuilder, der DataRelation, der DataTable und der ForeignKeyConstraint Klasse beschäftigen. In diesem Artikel geht es weiterhin auch nur um die Visualisierung der Daten mit anschliessender Navigation. Im nächsten Teil wird dann die Editierung und das Speichern dieser Daten erörtert.
Der Vorgang ist generell immer gleich, jedoch muss man bei ASP .net ein bisschen mehr Code Implementieren, da wir hier immer das DataSet Objekt auf dem Server verlieren.
1. Aufbauen einer Connection zu Datenquelle
2. Command für Master Tabelle aufbauen
3. DataAdapter für Master Tabelle aufbauen
4. Command für Detail Tabelle aufbauen
5. DataAdapter für Detail Tabelle aufbauen
6. DataRelation zum DataSet hinzufügen um die Relation zwischen Master / Detail Tabelle zu bekommen
7. Daten durch den / die DataAdpater abholen
8. Master Tabelle an Controls binden
9. Navigationselemente und deren CodeBehind
10. Detail Tabelle an ein DataGrid Control binden


Aufbauen der Verbindung zu Datenquelle:

Importieren Sie die Namespaces System.Data und System.Data.SqlClient, wenn Sie auf einen OleDB Datenquelle gehen, müssen Sie dementsprechend eine OleDB Connection aufbauen.
 protected System.Data.SqlClient.SqlConnection connSQL;
//Erzeugen einer neuen Verbindung zum localen SQL Server und der Northwind Sample DataBase
connSQL = new SqlConnection("server=w2k_dotNET; UID=sa; PWD=; database=Northwind;");

Das Command Objekt für die Master Tabelle

 protected System.Data.SqlClient.SqlCommand cmdSqlCustomers;
//Abfrage der Master Datensätze
string strSqlCustomers = "SELECT * FROM Customers";
cmdSqlCustomers = new SqlCommand(strSqlCustomers, connSQL);
 

DataAdapter für die Master Tabelle

 //DataAdapter mit den Master Datensätzen
daCustomers = new SqlDataAdapter(cmdSqlCustomers);
 

Command Objekt für die Detail Datensätze

 protected System.Data.SqlClient.SqlCommand cmdSqlOrders;
//Abfrage der Detail Datensätze
string strSqlOrders = "SELECT * FROM Orders";
cmdSqlOrders = new SqlCommand(strSqlOrders, connSQL);
 

DataAdapter für die Detail Taballe  

 protected System.Data.SqlClient.SqlDataAdapter daOrders;
//DataAdapter mit den Detail Datensätzen
daOrders = new SqlDataAdapter(cmdSqlOrders);
 

Hinzufügen einer DataRelation zu einem DataSet


Das DataRelation Objekt ist ein sehr interessantes Objekt und repräsentiert eine Parent / Child Beziehung zwischen zwei DataTable Objekten. Dabei wird die Relation auf zwei DataColoumn Objekte, eine DataColoumn in der Master Tabelle und eine DataColoumn in der Detail Tabelle erzeugt. Mann kann diese Relation als Primary Key / Foreign Key Relation sehen. Dabei ist die Relation einfacher einzusetzen als das ForeignKeyContraint Objekt. Das ForeignKeyConstraint hingegen ist mehr zu gebrauchen wenn Sie Unique Constraints auf eine Tabelle legen wollen. Das Relation Objekt ist einfacher einzusetzen, denn wenn eine Relation auf zwei Spalten gesetzt wird erzeugt das DataRelation Objekt automatisch die PrimaryKey und ForeignKey Eigenschaften innerhalb des DataSets. Nach dem erfolgreichem Hinzufügen einer Relation kann man recht einfach durch die Master / Detail Datensätze navigieren. Um eine Relation hinzufügen zu können muss ein neues DataSet Objekt angelegt werden:

 protected System.Data.DataSet dsNorthwind = new DataSet();
 
 protected System.Data.DataRelation drCustOrders;
//Hinzufügen einer DataRelation zwischen den Customers und ihren Bestellungen
drCustOrders = new DataRelation("drCustOrders", new DataColumn[]{dsNorthwind.Tables["Customers"].Columns["CustomerID"]}, new DataColumn[]{dsNorthwind.Tables["Orders"].Columns["CustomerID"]},false);
dsNorthwind.Relations.Add(drCustOrders);
 

Daten von der Datenquelle mit den DataAdaptern abholen:


Dazu muss natürlich ein DataSet Objekt vorhanden sein in das wir die Daten ?pushen?. 
 

 daCustomers.Fill(dsNorthwind, "Customers");
daOrders.Fill(dsNorthwind, "Orders");
 

Binden der Master Datensätze an Controls


In meinem Beispiel werde ich die Datensätze an asp:TextBoxen binden.

 
 <asp:Button id="btnLoad" style="Z-INDEX: 101; LEFT: 16px; POSITION: absolute; TOP: 16px" runat="server" Text="Daten laden"></asp:Button>
   <asp:Label id="Label1" style="Z-INDEX: 102; LEFT: 24px; POSITION: absolute; TOP: 72px" runat="server">Kundennr.:</asp:Label>
   <asp:TextBox id="txtCustId" style="Z-INDEX: 103; LEFT: 88px; POSITION: absolute; TOP: 72px" runat="server" Text='<%# DataBinder.Eval(dsNorthwind, "Tables[Customers].DefaultView.[" + ((int)Session["iRowCounter"]) + "].CustomerID") %>'>
   </asp:TextBox>
   <asp:Label id="Label2" style="Z-INDEX: 104; LEFT: 24px; POSITION: absolute; TOP: 112px" runat="server">Stadt</asp:Label>
   <asp:TextBox id="txtCity" style="Z-INDEX: 105; LEFT: 88px; POSITION: absolute; TOP: 112px" runat="server" Text='<%# DataBinder.Eval(dsNorthwind, "Tables[Customers].DefaultView.[" + ((int)Session["iRowCounter"]) + "].City", "{0}")%>'>
   </asp:TextBox>
   <asp:Label id="Label3" style="Z-INDEX: 106; LEFT: 24px; POSITION: absolute; TOP: 152px" runat="server">Land</asp:Label>
   <asp:TextBox id="txtCountry" style="Z-INDEX: 107; LEFT: 88px; POSITION: absolute; TOP: 152px" runat="server" Text='<%# DataBinder.Eval(dsNorthwind, "Tables[Customers].DefaultView.[" + ((int)Session["iRowCounter"]) + "].Country", "{0}")%>'>
   </asp:TextBox>
   <asp:Label id="Label4" style="Z-INDEX: 108; LEFT: 296px; POSITION: absolute; TOP: 72px" runat="server">Firma</asp:Label>
   <asp:TextBox id="txtCompany" style="Z-INDEX: 109; LEFT: 376px; POSITION: absolute; TOP: 72px" runat="server" Text='<%# DataBinder.Eval(dsNorthwind, "Tables[Customers].DefaultView.[" + ((int)Session["iRowCounter"]) + "].CompanyName", "{0}")%>'>
   </asp:TextBox>
 

Um eine Navigation zu ermöglichen habe ich den aktuellen Row Index in eine Session Variable abgelegt, ich weiss, dass ist nicht die feine englische, but it works!

Die Navigationselemente
Hierbei handelt es sich um vier einfache asp:Button?s die ich mit einer einfachen Logik versehe. Die Logik ermittelt den aktuellen Index innerhalb der Master Tabelle und erhöht, bzw. erniedrigt diesen. Danach werden die Daten neu aufgebaut und visualisiert.


 
  private void btnNextRecord_Click(object sender, System.EventArgs e) {
   if((int)Session["iRowCounter"] < ((int)Session["dsRowCount"])-1) {
    Session["iRowCounter"] = ((int)Session["iRowCounter"] )+1; 
   }
   else {
   Session["iRowCounter"] = 0;
   }
   LoadData();
  }
  private void btnPrevRecord_Click(object sender, System.EventArgs e) {
   if((int)Session["iRowCounter"] > 0) {
    Session["iRowCounter"] = ((int)Session["iRowCounter"] )-1; 
   }
   else {
    Session["iRowCounter"] = ((int)Session["dsRowCount"]-1);
   }
   LoadData();
  }
  private void btnLastRecord_Click(object sender, System.EventArgs e) {
   Session["iRowCounter"] = ((int)Session["dsRowCount"]-1);
   LoadData();
  }
  private void btnFirstRecord_Click(object sender, System.EventArgs e) {
   Session["iRowCounter"] = 0;
   LoadData();
  }
 

Zum Schluss der Hype. Hier wird nun gezeigt wie die Navigation, bzw. auch das Ermitteln von Detaildatensätzen von einem Masterdatensatz funktioniert. Der Vorgang gliedert sich in die Erfassungsstuffe des Masterdatensatzes in eine DataRow und dem Aufruf der Funktion CreateChildView(). Dieser Methode muss nur mitegegeben werden nach welcher Relation die Daten gefiltert werden sollen, also in unserem Beispiel die drCustOrders DataRelation:

 
System.Data.DataView parentRows;
System.Data.DataView childRows;
System.Data.DataRowView currentParentRow;
parentRows = new DataView();
parentRows.Table = dsNorthwind.Tables["Customers"];
currentParentRow = parentRows[iMasterIndex];
childRows = currentParentRow.CreateChildView("drCustOrders");
DataGrid1.DataSource = childRows;
DataGrid1.DataBind();
DataGrid1.Visible = true; 

Erfasst am: 30.04.2002 - Artikel-URL: http://www.devtrain.de/news.aspx?artnr=759
© Copyright 2003 ppedv AG - http://www.ppedv.de