ADO.NET Ralf Westphal Freier Fachautor & Berater MSDN Regional Director [email protected] Ausgangsfrage Wie wollen Sie in OO-Programmen auf Ihre Daten zugreifen? • • Per relationalem Datenzugriffs-API • Cursor, Join etc. Per Objektmodell • Objekte, „Collections“, Hierarchien Sind Datenbanksysteme nötig? Kann Datenverwaltung nicht über Objektmodelle stattfinden? • • • Intuitive Abbildung der „realen Welt“ Einfache zu Traversieren/Manipulieren 1 Datenmodell für persistente Daten und inmemory Datenverwaltung, statt 2 Probleme • • • • Persistenz Abfragen Gleichzeitiger Zugriff Große Datenmengen Lösungen Lösungen für mögl. Probleme mit Objektmodellen für die Datenhaltung • • • • Persistenz • Serialisierung (.NET Formatter) Abfrage • • Deklarative Abfragesprache Meta-Objektmodell Gleichzeitiger Zugriff • • (Objektmodell-)Server Clients cachen Daten Große Datenmengen • Server mit beliebiger interner Datendarstellung Fazit Datenbanken sind notwendig • Wertvolle Dienstleistungen • Transaktionen, gleichzeitiger Zugriff, Verwaltung großer Datenmengen, Replikation usw. Objektmodelle stehen nicht im Gegensatz zu Datenbanken Wir brauchen... • • ein vernünftiges Meta-Objektmodell ein zum Meta-Objektmodell passenden Datenbankzugriffs-APIs ADO.NET Grundlagen ADO.NET Architektur VS .NET Designers “<xml> … </xml>” Web/Windows Form Controls myDataSet <xml> … </xml> Cust Order Item VS .NET Class Generator Managed Provider DataAdapter DataReader Command Connection String Stream XmlReader TextReader XmlText- XmlNodeReader Reader XmlDocument ADO vs ADO .NET ein Überblick der Änderungen COM Marshalling Connection oriented OLE DB Provider Zwei mögliche Programmiermod.: • ADO • OLE DB nativ Cursor Joins für > 1 Tabelle Datentypen von COM/COM+ abhängig COM+, Datasets Disconnected Access Managed Providers Ein Modell: • Managed Provider (connected Layer) • DataSet (discon.) (Kein Cursor) Kein Join notwendig XML, keine Datentypen Konvertierung nötig Managed Providers Interaktion mit Datenquellen „managen“ Äquivalent des OLE DB Layers • Direkte Darstellung des Consumer Interfaces (nicht mehr mit der Zweiteilung COM/Automation) Aktuelle Implementierungen • • • OleDB Managed Provider (ähnlich ADO) • Zugriff auf beliebige OLE DB Provider SQLServer Managed Provider Weitere folgen • • Data Provider ODBC SQL XML Data store Einbindung in die Objekthierarchie ... System.Data System.Data.OleDb System.Data.SqlClient ... System.Data System.Data.OleDb System.Data.SqlClient DataRelation OleDbConnection SqlConnection DataSet OleDbCommand SqlCommand DataTable OleDbDataReader SqlDataReader DataRow OleDbParameter SqlParameter DataColumn OleDbDataAdapter SqlDataAdapter DataSetView OleDbErrors SqlErrors ... ... ADO.NET Objekt Model Das klassische ADO Gewand … • • Connection Command, Parameter … mit neuen Objekten: • • • DataReader • Forward-only, Read-only „Recordset“ DataSet • Disconnected, In-Memory Cache DataAdapter • Verbindet das DataSet mit der Datenquelle Connection Objekt Repräsentation einer Verbindung zu einer Datenquelle Mit einer Connection ist es möglich … • • Die Verbindung zur Datenquelle anzupassen Transaktionen zu handhaben (Begin, Commit, Abort) Ähnlichkeiten zum ADODB.Connection Objekt sind nicht unerwünscht Connection Objekt // Angabe des Namespace (System.Data.SQL) Using System.Data.SqlClient; // Instanziieren eines SQLConnection Objekts SqlConnection cnn = new SqlConnection(); // Connection String setzen cnn.ConnectionString = "server=localhost;uid=sa;database=pubs"; // Öffnen der Connection cnn.Open(); Command Objekt Stellt ein auszuführendes Kommando dar • Mit dem ADO .NET Command Objekt ist es möglich: • • • Nicht unbedingt ein SQL Kommando Ein Statement, welches auf dem Server ausgeführt werden soll, zu definieren Parameter Informationen für dieses Kommando anzugeben Rückgabewerte nach der Kommandoausführung zu erhalten Wie das ADODB.Command Objekt Kann Parameter enthalten • Werte, die bei Ausführung eines Statements genutzt werden können Command Objekt // Create Command SqlCommand cmd = new SqlCommand(); // Aktive Connection des Kommandos und Inhalt setzen cmd.ActiveConnection = cnn; cmd.CommandText = "Select au_lname from authors where state = @param1"; // Parameter erzeugen und Werte setzen cmd.Parameters.Add( new SQLParameter("@param1", typeof(String),2) ); cmd.Parameters["@param1"].Value = "CA"; DataReader Der DataReader bietet einen forward-only, read-only Datenstrom • Stellt die Ergebnisse einer ausgeführten Abfrage/Kommandos dar Der DataReader bietet die Möglichkeit … • Einen Ergebnis-Datenstrom von einer Datenquelle zu erhalten Gleichbedeutend mit einem FO/RO RecordSet • • Unterstützt jedoch weder Scrolling noch Updates Auf Felder greift man am besten mit Hilfe von Accessoren (strongly typed, indexed) zu, die FieldsCollection ist die schlechtere Möglichkeit • • Performance myRow.GetInt(0) Zugriff über den Feldnamen (einfache Nutzung/Kompatibilität) myRow["fieldname"] Unterstützung von DataBinding in WebForms DataReader Verwendung // DataReader Definieren IDataReader dr; // Kommando ausführen cmd.Execute(out dr); // Ergebnisse auslesen while(dr.Read()) { Console.WriteLine("Name = " + dr["au_lname"]); } // Connection schließen cnn.Close(); DataSet Common client data store Relationale Sicht der Daten • Tabellen, Spalten, Zeilen, Beschränkungen, Beziehungen Direkte Erzeugung von Metadaten einfaches Einfügen von Daten Explizites Cache Modell • • • • DataSet Tables Table Columns Column Constraints Constraint Rows Relations Row Relation Disconnected, remotable Objekt Hat keine Kenntnis über die Datenquelle oder deren Eigenschaften Zugriff wie auf ein Array Strong Typing möglich DataSet // Erzeugen eines DataSet "PublicSet" DataSet pubs = new DataSet(" PublicSet"); //Erzeugen einer Tabelle “bestand" DataTable inventory = new DataTable(“bestand"); inventory.Columns.Add(“kennzeichenID",typeof(Int32)); inventory.Columns.Add(“menge",typeof(Int32)); // Tabelle “Bestand” zum DataSet PublicSet hinzufügen pubs.Tables.Add(bestand); // Datensatz zur Bestandstabelle hinzufügen DataRow row = bestand.NewRow(); row[“kennzeichenID"]=1; row[“menge"]=25; bestand.Rows.Add(row); (Strongly) Typed DataSet DataSets, Tabellen, Zeilen als Objekte nutzen • Spalten und Beziehungen als Eigenschaften //Ausgabe jedes Autors und dessen Titel foreach (Author myAuthor in Pubs.Authors.Rows) { Console.WriteLine("Name = " + myAuthor.au_lname); foreach (Title myTitle in myAuthor.Titles) { Console.WriteLine("Title = " + myAuthor.Title); } } DataAdapter Weiß, wie eine Tabelle aus der Datenbank geladen wird und schreibt Änderungen zurück • • • • • Fill(DataSet) Update(DataSet) Mapping zwischen Tabellen und Spalten Benutzer kann die voreingestellten Kommandos überschreiben (insert/update/delete) • • z. B. um Stored Procedures anzugeben Default-Kommandos mit CommandBuilder erzeugen Erlaubt es, ein DataSet aus mehreren Datenquellen zu füllen DataAdapter // Neues DataSetCommand SqlDataAdapter dsAdap = new SqlDataAdapter( "Select * from authors",cnn); // Daten an ein DataSet übergeben dsAdap.Fill(pubs, "Authors"); // Änderungen in den Kundendaten des DataSets durchführen und Update durchführen pubs.Tables["Authors"].Rows[0]["au_lname"]="smith"; SqlCommandBuilder bld = new SqlCommandBuilder(dsAdap); dsAdap.Update(pubs, "Authors"); DataBinding DataView • • • • Wie ein View auf eine Tabelle Erlaubt, Sortierreihenfolge und Filter in einem View einer Tabelle festzulegen Beliebige DataViews können von einer Tabelle erzeugt werden, um unterschiedliche Views der gleichen Tabelle möglich zu machen Wird für das DataBinding verwendet DataSetView • • • • Wie ein View, daß auf einem DataSet aufsetzt Sortierreihenfolge und Filter lassen sich setzen Erlaubt das Verbinden von DataViews Wird für das Databinding verwendet DataBinding Als Quelle für DataBinding dienen: • • • • • • • • • (DataReader) DataTable DataView DataSet DatSetView Array Collection IList IEnumerable Zusammenfassung ADO .NET ist die natürliche Weiterentwicklung von ADO • • Bekanntes Connection/Command Modell Teilung von Persistenz und Programmierung • • Optimierter ForwardOnly/ReadOnly Ergebnis-Datenstrom Expliziter, nicht verbundener relationaler Cache ADO .NET ist XML optimiert ADO .NET ist im .NET Framework integriert • Exception Handling, Namensgebung, Notierung Bessere plattformübergreifende Zusammenarbeit und Sharing der Daten, bessere Skalierbarkeit, strong typing ADO.NET Anwendungen Überblick Szenen eines Datenzugriffs • • • Aufwärmen: • • • AutoNumber-Felder Logisches Löschen Joins & DataSets Jetzt wird es ernst: • • Hierarchische Daten laden Logisches Sperren Cool down: • Eigene Datenquellen anbinden AutoNumber-Felder AutoNumber-Information wird nicht autom. geladen • Steht erst nach DataAdapter.FillSchema zur Verfügung DataColumn.AutoIncrementSeed/.AutoIn crementStep müssen gesetzt werden AutoNumber-Werte in DataTable können nicht in AutoNumber-DB-Felder persistiert werden • Fremdschlüssel geraten aus dem Tritt Fazit: AutoNumber-Felder vermeiden! Logisches Löschen Ein Datensatz wird im DataSet gelöscht, in der Datenbank jedoch nur als gelöscht markiert Lösung: • • Tabelle mit einer Spalte für ein Löschkennzeichen ausstatten DataAdapter mit speziellem Löschkommando ausstatten • Update mytable set delFlag=@delFlag where pk=@pk Joins & DataSets Einsatz bisher • • Anbinden von untergeordneten Informationen (Master/Detail) Anbinden von Lookup-Informationen Kein (semi)automatischer Update • CommandBuilder generiert keine DML-Anweisungen für DataTables mit mehreren Tabellen Das ist gut so! Einsatz mit DataSets • • Untergeordnete Informationen über Relationen anbinden • • Es müssen mehrere Select-Anweisungen ausgeführt werden Where-Klauseln untergeordneter Select-Anweisungen müssen Where-Klauseln übergeordneter enthalten Lookup-Informationen entweder auch über Relationen oder weiterhin über Joins anbinden Fazit: Joins verlieren mit DataSets an Bedeutung • Joins sind oft keine natürliche Darstellung von Datenbeziehungen Hierarchische Daten laden Hierarchische Daten bilden oft eine logische Einheit: „Dokumente“ • • DB-APIs kennen keine „Dokumente“ Daten in „Dokument“-Granularität zu laden, entlastet Netzwerkverbindungen CRUD-Szenarien profitieren vom Denken in „Dokumenten“ • • • „Dokumente“ definieren... Zugriff per „Dokument“-ID (PK des Wurzeldatensatzes) „Dokumente“ mit eigenem API verwalten... Hierarchische Daten laden Hierarchische Daten laden Mögliche „Dokument“-Definition <table name="customer" basetable="customers" fields="*" pk="custID" checkOutOk="1"> <table name="invoices" basetable="invoices" fields="*" pk="invID" checkOutOk="1"> <table name="lineItems" basetable="invoiceLineItems" fields="*" pk="invLIID"> <lookup basetable="products" fields="description, price" pk="prodID"/> <column name="total" type="System.Double" expression="qty*price"/> </table> <column name="total" type="System.Double„ expression="sum(child(lineItems).total)"/> </table> <table name="comm" basetable="customercommunication" fields="telID, custID, tel" pk="telID"/> </table> Hierarchische Daten laden Rudimentärer „Dokument“-API • • • • DataSet CreateDocument() DataSet GetDocument(string id) StoreDocument(doc as DataSet) DeleteDocument(string id) ToDos • • • • „Dokument“-Definition zuordnen DB-Anbindung Validation? Logisches Sperren? Logisches Sperren Sperren von Datensätzen während einer (lange andauernden) Bearbeitung Probates Mittel: physikalisches Sperren via DB-API • • Das ist immer falsch! ADO.NET bietet dafür keine Mittel Lösung: Logisches Sperren • Sperrungen werden durch Anwendung verwaltet • Z.B. im Hauptspeicher, spezielle Tabelle Logisches Sperren Herausforderungen • Sicherstellen, dass alle Beteiligten die logischen Sperren beachten • • • • Datenzugriff darf nicht mehr direkt stattfinden, sondern nur über einen dedizierten API Wer (ent)sperrt wann? Was passiert mit „zu lange“ gesperrten Daten? • Z.B. weil der Client abgestürzt ist Performantes Sperren vs „dauerhaftes“ Sperren Eigene Datenquellen anbinden DataSets werden über Managed Provider gefüllt • • Managed Provider Klassen • • • DataSets sind unabhängig von Datenquellen Managed Provider sind nicht auf Datenbanken festgelegt Datenquelle anbinden/manipulieren: Connection, Command Datenquelle lesen: DataReader, DataAdapter Datenquelle aktualisieren: DataAdapter A Simple Managed Provider • Realisierung eines eigenen DataAdapter ist ausreichend • Implementiert IDataAdapter Fazit ADO.NET zwingt zum Umdenken • • • • Es gibt (fast) keine Cursor mehr • DataSets sind in-memory Datencaches DataSets unterstützen eine oft natürlichere Sicht auf Daten ADO.NET bietet kaum „Infrastruktur“ für einige typische Probleme • • DataReader ist heute eine „Ausnahme“ „Dokument“-Handling SQL XML .NET Klassen mögen helfen Logisches Sperren Die Zukunft? • • ResultSets ObjectSpaces: OR-Mapping Fragen!? Uff... ADO .NET Quellen Jetzt lerne ich ADO.NET Ralf Westphal, 400 Seiten, Markt+Technik, 2002 (noch nicht erschienen) ADO .NET for the ADO Programmer http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/adonetdev.asp ADO.NET : Migrating from beta 1 to beta 2 http://www.asptoday.com/content/articles/20010802.asp?WROXEMPTOKEN=934243ZaVjEiid0J0l 7LCYvBGK Coping with a New Beta - Connecting to Databases http://www.dotnetjunkies.com/tutorials.aspx?tutorialid=81 Coping with a New Beta - DataSetCommand to DataAdapter http://www.dotnetjunkies.com/tutorials.aspx?tutorialid=83 Using ADO+ and C# in the .NET Framework - Part 1 http://www.asptoday.com/content/articles/20000925.asp?WROXEMPTOKEN=934243ZaVjEiid0J0l 7LCYvBGK Using ADO+ and C# in the .NET Framework - Part 2 http://www.asptoday.com/content/articles/20000925.asp?WROXEMPTOKEN=934243ZaVjEiid0J0l 7LCYvBGK Revisiting the Use of ADO in .NET Applications http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndive/html/data08092001.asp Commands in ADO .NET http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndive/html/data07262001.asp Data Relations and Relatives http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndive/html/data07122001.asp Views and Filters http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndive/html/data06142001.asp Paradigmenwechsel mit ADO.NET http://www.microsoft.com/germany/ms/msdnbiblio/kolumne/062001rw.htm Über den Referenten Ralf Westphal ist freier Softwaretechnologievermittler. Er arbeitet als Fachautor, Coach/Berater, Softwareentwickler und Sprecher auf Konferenzen im In- und Ausland wie Microsoft Technical Summit, XML-in-Action, BASTA!, COMDEX, Software Development oder XML One. Der Schwerpunkt seiner Arbeit liegt bei der Vermittlung und Anwendung moderner Softwaretechnologien und -konzepte auf der Microsoft Plattform mit Fokus in den Bereichen OOP/komponentenorientierte Entwicklung, Softwarearchitektur und .NET Framework. Darüber hinaus ist Ralf Westphal einer der deutschen Microsoft MSDN Regional Directors, Mitglied verschiedener Fachbeiräte und war von 1998 bis 2001 Chefredakteur der Visual Basic Fachzeitschrift BasicPro. Bücher des Referenten .NET kompakt 140 Seiten, Spektrum Akademischer Verlag, 2002, ISBN 3827411858 Jetzt lerne ich ADO.NET Einfache Datenbankprogrammierung im .NETFramework 400 Seiten, Markt+Technik, 2002, ISBN 3827262291 (erscheint Mitte 2002) Empower people through great software any time, any place, and on any device