Objektorientiertes Programmieren mit .NET und C# Datenbanken

Werbung
Objektorientiertes Programmieren mit .NET und C#
Datenbanken
Simon Bastian
Abstract: Der Datenbankzugriff im .NET Framework wird durch die ADO.NET Objekte abgewickelt. Durch ADO wird die nötige Basisfunktionalität geboten und seit
.NET 3.5 SP1 werden ebenfalls erweiterte Funktionen angeboten, wie z.B. Objekt relationiales Mapping.
Im folgenden wird kurz auf die einzelnen Objekte von ADO.NET eingegangen und an
einem Beispiel veranschaulicht wie ein Datenbankzugriff mit C# und ADO.NET aussehen
könnte. Weiterhin wird das Konzept des Objekt relationalem Mappings kurz erklärt und
die Umsetzung in ADO.NET sowie Alternativen vorgestellt.
1
ADO.NET
Die ADO.NET Bibliotheken enthalten Klassen für die Verbindung und Übermittlung von
Abfragen an Datenquellen, sowie die Verarbeitung von Ergebnissen.
Im Normalfall wird ADO.NET zum Einsatz kommen, wenn eine .NET Anwendung mit
einer Datenbank interagiert.
Merkmale von ADO.NET sind:
• Stabilität (.NET Plattform)
• Unabhängigkeit von Datenbankerstellern
• Plattformunabhängigkeit und Integration verteilter Komponenten (XML)
• Skalierbarkeit und Performance (Datasets)
Die Klassen von ADO.NET sind im Namespace System.Data zu finden. Datenprovider findet man in weiteren Namespaces wie z.B. System.Data.SqlClient oder System.Data.OleDB.
2
Objektmodell
Das ADO.Net Objektmodell ist, wie in Abbildung 1 erkennbar, in zwei Teile unterteilbar.
Einen verbunden- und einen unverbundenen Block. Der verbundene Block, in der Graphik
die linke Hälfte, hat eine Verbindung zur Datenbank. Dieser Block stellt die typischen
Funktionen für Datenbankzugriffe zur Verfügung.
Und der unverbundene Block, in der Graphik die rechte Hälfte, bei dem keine Verbindung
besteht. Der unverbundene Teil ist eine Besonderheit die man nicht in jeder Programmiersprache findet. Es wird die Möglichkeit geboten Daten aus der Datenbank zu sichern, zu
bearbeiten und wieder in die Datenbank zu integrieren.
Der DataAdapter fungiert als Brücke zwischen beiden Blöcken.
Abbildung 1: Das ADO.Net Objektmodell
3
Connection Objekt
Durch das Connection Objekt wird eine Verbindung zur Datenbank repräsentiert, es stellt
die Methoden .Open() und .Close() bereit zum Herstellen einer Verbindung und zum
Schließen dieser. Mittels einer Verbindungszeichenfolge ist es möglich ein solches Objekt zu erstellen.
Um z.B. eine Verbindung mit der lokalen Instanz SQLEXPRESS und der Datenbank Northwind aufzubauen könnte man wie folgt vorgehen:
connectionString = @"DataSource=.\SQLEXPRESS;Initial Catalog=Northwind;"
+"Integrated Security=SSPI";
SqlConnection = new SqlConnection(connectionString)
3.1
Builder für Verbindungszeichenfolgen
Da es nicht immer möglich ist Verbindungszeichenfolgen zur Entwurfs- oder Laufzeit zu
erstellen bietet ADO.NET einen Builder für diese an. Die vorige Verbindungszeichenfolge
könnte auch so erstellt werden:
SqlConnectionStringBuilder conBuild = new SqlConnectionStringBuilder();
conBuild.DataSource = @".\SQLEXPRESS";
conBuild.InitialCatalog = "Northwind";
conBuild.IntegratedSecurity = true;
Es wäre z.B. denkbar dass die Anwendung die Verbindungsinformationen aus einer Konfigurationsdatei bezieht und die Logindaten per Eingabe in einem Formular übermittelt
bekommt.
Weiter wichtige Eigenschaften des Builders sind UserID“ und Password“.
”
”
Durch den Aufruf von .ToString() oder .ConnectionString() wird eine gültige Verbingungszeichenfolge zurückgegeben, die dem Connection Objekt übergeben werden kann.
3.2
Verbindungspooling
ADO.NET führt intern ein Verbindungspooling durch, dass bedeutet offene Verbindungen
werden nach dem Schließen noch nicht direkt geschlossen, sondern sie werden in einem
Pool gespeichert. Wird nach längerer Zeit nicht mehr auf die Verbindung zugegriffen wird
sie geschlossen. Sollte die Verbindung allerdings wieder benötigt werden ist sie bereits
vorhanden.
Dies kann bei vielen Verbindungen zu erheblichen Leistungsverbesserungen führen.
4
Command Objekt
Die Klasse SqlCommand dient zum Ausführen von Abfragen im ADO.NET Objektmodell.
Es gibt verschiedene Möglichkeiten ein Command Objekt zu erzeugen die bei anderen
Objekten sehr ähnlich sind, deswegen werden sie der Vollständigkeit halber aufgezählt:
// Methode des Connection-Objekts
SqlCommand command = connection.CreateCommand();
// parameterloser Konstruktor
SqlCommand command = new SqlCommand();
command.Connection = connection;
// In Beiden fällen folgt die Zuweisung des eigentlichen Befehls
command.CommandText = queryString;
//Alternative: ein parametrisierter Konstruktor
SqlCommand command = new SqlCommand(queryString, connection);
4.1
Methoden
Methode
Execute Reader
ExecuteNonQuery
ExecuteScalar
Prepare
Cancel
BeginExecute...
EndExecute...
Funktionalität
Ruft Ergebnisse in einem SqlDataReader ab.
Erwartet keine Rückgabe (Insert, Update, Delete, Stored Procedures)
Führt Abfrage aus, ruft erste Spalte der ersten Zeile ab.
Bereitet eine Version der Abfrage vor.
Bricht die Ausführung der Abfrage ab.
Startet asynchrone Abfrage.
Beendet Ausführung der asynchronen Abfrage.
Durch BeginExcuteReader wird die asynchrone Ausführung eines DataReaders gestartet.
Der Vorteil an der asynchronen Ausführung kann sein, dass nicht auf jeden einzelnen
Befehl gewartet werden muss, sondern dass die Befehle asynchron ausgeführt werden.
4.2
Parametrisierte Abfragen
ADO.NET unterstützt parametrisierte Abfragen, d.h. es ist möglich in Abfragen Variablen zu nutzen deren Belegung nicht statisch ist. Ein Anwendungsfall wäre z.B. ein Login
bei dem ID und Password eingegeben und mit der Datenbank vergleichen werden sollen. Durch eine parametrisierte Abfrage kann man SQL Injections vorbeugen, da es nicht
möglich ist den Befehl zu escapen.
queryString = "SELECT COUNT(*) FROM Users"
+"WHERE ID = @ID AND PW = @PW";
command.Parameters.AddWithValue("@ID", "admin");
command.Parameters.AddWithValue("@PW", "passwort");
Natürlich sollten im Normalfall keine hart codierten Daten hinterlegt werden, sondern
beispielsweise bei einem Login die Nutzereingaben verwendet werden.
5
DataReader
Der DataReader ist ein effizientes und kompaktes Objekt, das einen sequentiellen Lesezugriff auf die Daten ermöglicht. Die Klasse ähnelt sehr anderen Readern in .NET wie z.B.
dem StreamReader oder dem TextReader. Das Objekt wird durch den Aufruf der Methode
ExecuteReader des Command Objektes initialisiert.
Man sollte unbedingt sobald der Reader nicht mehr benötigt wird die .Close() Methode
aufrufen, um ungewollte Verbindungsprobleme zu vermeiden.
Typischerweise verwendet man einen DataReader wenn man nur lesenden Zugriff auf Datensätze benötig, da er einfach am leichtgewichtigsten ist.
Eine Anwendung des DataReaders könnte wie folgt aussehen:
SqlDataReader reader = command.ExecuteReader();
while(reader.read())
{
Console.WriteLine("ID: {0}", reader["CustomerID"]);
}
reader.Close();
6
DataSet
Eigentlich ist das DataSet Objekt eine Datenmenge, die Daten sind allerdings nicht mit der
Datenbank verbunden. Im Gegensatz zum DataReader kann man beliebig auf die Daten
zugreifen. Es ist möglich zu sortieren, zu suchen und zu filtern.
Änderungen lassen sich zwischenspeichern, auf relationale Fehler überprüfen und gegen
den Stand der Datenbank vergleichen. Zusätzlich ist es möglich die geänderten Daten in
die Datenbank zu migrieren.
Da in ADO.NET DataSet Objekte und XML Dokumente fast gleich sind, ist es leicht
möglich zwischen beiden Datenstrukturen zu konvertieren.
6.1
Modell
Das DataSet, siehe Abbildung 2, besteht aus DataTables die den eigentlichen Relationen
in der Datenbank entsprechen, Relations den Beziehungen zwischen den Relationen, und
XML Schema Informationen.
Die DataTables lassen sich weiterhin in einzelene Einträge (DataRows) und Felder (DataColumns) unterteilen, mit ihren Einschränkungen (Constraints).
Abbildung 2: Das ADO.NET Dataset
6.2
Methoden des DataSets
Methode
Clear
Load
Merge
GetXml
WriteXmlSchema
6.3
Funktionalität
Entfernt alle DataRow Objekte.
Lädt Daten von einem DataReader.
Führt Daten mehrer DataSets zusammen.
Gibt den Inhalt als XML aus.
Schreibt das Schema als XML in eine Datei.
DataTable
Das DataTable Objekt stellt eine Relation mit den zu erwartenden Inhalten da.
Die DataTable stellt eine Reihe von verschiedenen Methoden bereit, die gebräuchlichsten
davon sind:
Methode
Funktionalität
NewRow
Gibt ein neues DataRow Objekt für die Table zurück.
Reset
Setzt die Table auf den ursprünglichen Status zurück.
Load
Lädt Daten von einem DataReader.
Select
Gibt ein Array mit DataRows zurück.
GetErrors
Gibt ein Array mit fehlerhaften DataRows zurück.
ImportRow
Importiert eine bestehende DataRow.
CreateDataReader Erstellt einen DataTableReader mit dem Inhalt der Table.
Das folgende Beispiel zeigt wie man einen bestehenden Eintrag eines DataTable Objekts
verändern kann unter Verwendung der Find Methode und DataRows:
DataTable table = DataTable("Customers");
DataRow row = table.Rows.Find("ALFKI")
if (row == null)
{
Console.WriteLine("Kunde wurde nicht gefunden");
}
else
{
row["CompanyName"] = "New Company Name";
}
6.4
DataView
Es ist möglich Daten aus einem DataSet durch die .Select() Methode des DataTable Objekts aufzurufen, dies aber nicht immer besonders effizient. Weiterhin kann man die Ergebnisse des .Select() Aufrufs nicht an Windows- oder Webformulare binden. Deswegen
wurde das DataView Objekt eingeführt, das beides ermöglicht.
DataTable table = new DataTable("TabellenName");
DataView view;
view = new DataView(table);
Einige wichtige Methoden des DataView Objekts sind:
DataView Methode
Find
FindRows
Sort
ToTable
CopyTo
7
Funktionalität
Durchsucht DataView nach einer DataRow.
Durchsucht DataView nach mehreren DataRows.
Legt die Sortierreihenfolge für den DataView fest, bzw. zeigt sie an.
Erzeugt eine DataTable aus dem DataView.
Kopiert DataView in ein Array.
DataAdapter
Die Klasse DataAdapter dient als Brücke zwischen den Daten in der Datenbank und einem
DataSet, das offline verfügbar ist.
Der DataAdapter kann in einem DataSet zwischengespeicherte Änderungen an die Datenbank übermitteln. Im Gegensatz zu einem Command Objekt wird das Öffnen und Schließen der Verbindung durch den DataAdapter verwaltet.
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter(queryString, connectionString);
da.Fill(ds);
Fill führt die Abfrage aus und speichert die Ergebnisse in einem DataSet. Durch die Methode .Update() können Änderungen an die Datenbank übermittelt werden.
8
Transaction
Bietet die Möglichkeit alle Änderungen an der Datenbank auf einmal zu übermitteln. Entweder sind alle Änderungen erfolgreich oder alle werden verworfen.
Ein Anwendungsszenario wäre z.B. eine Überweisung bei der das Geld erst dann gutgeschrieben wird, wenn das Abbuchen auch erfolgreich war.
SqlTransaction ta = connection.BeginTransaction();
SqlCommand command = connection.CreateCommand();
command.Transaction = ta;
try
{
command.CommandText = "INSERT INTO Customers.NewCol1 Values(’1’)";
command.ExecuteNonQuery();
command.CommandText = "INSERT INTO Customers.NewCol1 Values(’2’)";
command.ExecuteNonQuery();
ta.Commit();
} catch (Exception e)
{
ta.Rollback();
}
9
Beispiel
Das Query gegen die Beispieldatenbank Northwind ermittelt die Kunden, deren Bestellungen von Ms. Dodsworth (EmployeeID 9) bearbeitet wurden.
Der Einfachheit halber wird in dem Beispiel auf Parameter verzichtet und die Anfrage
wurde nicht optimiert.
Ebenfalls ist die Fehlerbehandlung für die Praxis in dieser Form nicht zu empfehlen, in den
Beispielen wurde bewusst auf eine gezielte Behandlung der möglichen Fehler verzichtet.
string connectionString = conBuild.ToString();
string queryString = "SELECT DISTINCT c.CustomerID, c.CompanyName"
+" FROM dbo.Customers c"
+" JOIN dbo.Orders o ON c.CustomerID = o.CustomerID"
+" JOIN dbo.Employees e ON o.EmployeeID = e.EmployeeID"
+" WHERE e.EmployeeID = 9"
+" ORDER BY c.CustomerID";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = connection.CreateCommand();
command.CommandText = queryString;
try
{
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}", reader[0], reader[1]);
}
reader.Close();
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
In der Konsole würden nun KundenID und Name der jeweiligen passenden Einträge ausgegeben werden.
Durch den DataReader werden Strings sequentiell gelesen und ausgegeben. Es handelt
sich in diesem Beispiel um keine Objekte, die zurückgegeben werden.
10
O/R Mapping
Bei steigender Komplexität der Logik wird es immer schwieriger eine Übersicht über die
Struktur zu bewahren.
Bisher im Bezug auf ADO.NET war es nötig das Mapping zwischen Logik und Daten
selbst vorzunehmen. Ein Mapper stellt eine Vermittlung zwischen Daten und Objekten da.
Ein relationales Modell wird in ein Objektmodell überführt.
In einem einfachen Fall werden Tabellen auf Klassen abgebildet. Jeder Datensatz enspricht
der Instanz eines Objekts.
Dabei müssen auch einige Punkte an das Objektmodell erfüllt werden, die von einem
relationalen Datenmodell normalerweise nicht vollständig unterstüzt werden:
• Objektidentität (Primärschlüssel kann auf unterschiedliche Art und Weise abgebildet werden)
• Vererbung
• Nebenläufigkeit
• Kapselung
Seit ADO.NET 3.5 SP1 ist das Entity Framework, ein O/R Mapper Bestandteil von ADO.NET.
11
11.1
ADO.NET Entity Framework
Allgemein
Das ADO.NET Entity Framework ist ein Objekt relationaler Mapper der seit Version 3.5
SP1 Bestandteil von .NET ist.
Durch einen in Visual Studio integrierten Assistenten wird eine sehr leichte Bedienung
ermöglicht, die für einfache Anwendungsszenarien ausreichend ist.
Im Vergleich zu LINQ werden z.B. N:M Relationen unterstütz. LINQ2SQL unterstützt
zunächst nur 1:1 Relationen, für komplexere Relationen wird eine Mappingtabelle benötigt.
Zusätzlich unterstützt das ADO.NET Entity Framework eine große Auswahl an Datenbankprovidern (MS SQL Server, Oracle, DB2, MYSQL, PostgreSQL).
Abbildung 3: Das ADO.NET Entityframework
CSDL
MSL
SSDL
Conceptual Schema Definition Language
Mapping Schema Language
Store Schema Definition Language
Wie in der Graphik (Abbildung 3) erkennbar greift die eigentliche Logik nur noch auf die
Conceptual“ Ebene zu.
”
Also die Ebene in der die Daten als Entitäten repräsentiert werden. Die eigentlichen relationalen Daten werden in der Logical Store“ Ebene repräsentiert.
”
Als Verbindung zwischen den beiden Ebenen dient die Mapping“ Ebene, die Informatio”
nen über das Mapping enthält.
11.2
ADO.NET EF Beispiel
Das vorige Beispiel wurde nun mit dem ADO.NET Entity Framework umgesetzt. Allerdings wurde hierbei nicht nur der Assistent genutzt, sondern auch einige Anpassungen
vorgenommen.
Auffallend ist hier das nun ein deutlich einfacheres Query das gewünschte Resultat liefert.
using (NorthwindEntities nw = new NorthwindEntities()) {
try
{
string queryString = "SELECT VALUE c FROM NorthwindEntities.Customers"
+" AS c WHERE c.EmployeeID = 9";
ObjectQuery<Customers> custQuery =
new ObjectQuery<Customers>(queryString, nw, MergeOption.NoTracking);
foreach (Customers result in custQuery)
{
Console.WriteLine("\t{0}\t{1}",result.CustomerID, result.CompanyName);
}
} catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Man sollte sich aber auf jedenfall vor Augen führen, dass obwohl diese Anfrage erheblich
einfacher ist, der Aufwand diese Anfrage zu ermöglichen um einiges höher ist als das
vergleichbare Aufwand für eine reine ADO.NET Abfrage.
12
Alternative O/R Mapper
Neben dem ADO.NET Entity Framework wurde mit .NET 3.5 auch LINQ eingeführt.
Allerdings gab es bereits vor dieser Entwicklung Objekt relationale Mapper für die .NET
Plattform. Beispielsweise die komerzielle Lösungen Genome oder das aus der Java Welt
portierte NHibernate.
Im folgen wird ein kurzer Überblick über LINQ und NHibernate geliefert. Dies bedeutet
aber nicht, dass Genome oder andere O/R Mapper keine Bedeutung haben. Um das Thema
aber nicht zu sehr in die Tiefe zu ziehen, wird auf eine detailliertere Übersicht verzichtet.
12.1
LINQ
LINQ (Language Integrated Queries) ist eine Komponente von .NET die die eine Datenabfrage direkt in die Programmiersprache integriert.
Ein wesentlicher Vorteil von LINQ ist, dass der Code bereits durch den Compiler auf Fehler überprüft wird. Eine Vielzahl von Datenquellen wird unterstützt, allerdings ist in diesem Zusammenhang hauptsächlich LINQ2SQL interessant. Auf die Kombinationen von
LINQ2Entities und LINQ2Datasets sei an dieser Stelle nur verwiesen, es wird nicht im
Detail darauf eingegangen.
LINQ2SQL ordnet dem Datenmodell einer relationalen Datenbank ein Objektmodell zu.
Es handelt sich hierbei also ebenfalls um einen O/R Mapper, der im Vergleich zu eigentständigen O/R Mappern allerdings nicht den selben Funktionsumfang bietet.
Durch einen in Visual Studio integrierten Designer wird eine einfache und schnelle Erstellung von Mapping Informationen ermöglicht.
Für weitere Informationen zu LINQ empfiehlt sich die Ausarbeitung zu diesem speziellen
Thema.
12.2
LINQ Beispiel
Das folgende Beispiel zeigt die Umsetzung des hier bereits öfter vorgestellten Beispiels
mit Hilfe von LINQ:
DataContext dc = new DataContext(connectionString)
Table<Customer> customers = dc.GetTable<Customer>();
Table<Employee> employees = dc.GetTable<Employee>();
Table<Order> orders = dc.GetTable<Order>();
var query = (
from c in customers
join o in orders on c.CustomersID equals o.CusomerID
join e in employees on o.EmployeeID equals e.EmployeeID
where e.Employee == 9
order by c.CustomerID
select new {c.CustomerID, c.CompanyName}
).Distinct();
foreach (var row in query)
{
Console.WriteLine("\t{0}\t{1}", row.CustomerID, row.CompanyName);
}
Auf den ersten Blick fällt bereits auf, dass hier ebenfalls als Rückgabetyp Objekte verwendet werden. Bezüglich des Umfangs der Abfrage gewinnt man aber nicht im Vergleich
zum ADO.NET Entity Framework.
12.3
NHibernate
Bei NHibernate handelt es sich um die .NET Portierung des bekannten Open Source Objekt relationalen Mappers Hibernate aus der Java Welt.
NHibernate zeichnet sich besonders durch folgende Merkmale aus:
• Kostenlose Open Source Software (GNU Lesser General Public License)
• Bietet mit HQL (Hibernate Query Language) eine eigene Abfragesprache
• Portierung von Hibernate (Erstes Release 2001) führt zu einem ausgereiften Entwicklungsstadium
• Teilweise Integration von LINQ
Ein großer Vorteil von NHibernate ist ohne Frage eine breite Community und eine über
die Jahre ausgereifte Software. Im Gegensatz zum ADO.NET Entity Framework ist es
bestimmt leichter zu speziellen Themen und Fragestellungen eine Antwort bzw. Lösung
zu finden.
Allerdings wird NHibernate nur noch von der Community entwickelt, dies bedeutet dass
es unter Umständen langsamer auf neue Entwicklung der .NET Plattform reagiert.
13
Vergleich ADO.NET - O/R Mapping
Vergleicht man die unterschiedlichen Umsetzungen des selben Beispiels fällt sehr schnell
auf, dass es erhebliche Unterschiede nicht nur in dem Umfang der Abfrage sondern auch
z.B. bei dem Zugriff auf die Daten gibt. Es stellt sich natürlich die Frage welche Art der
Umsetzung am meisten Sinn macht.
Dies lässt sich aber nicht generell beantworten, sondern ist von einigen Faktoren abhänging
wie z.B.:
• Performance
• Komplexität
• Umfang des Projekts
Der Hauptgrund für reine ADO.NET Anwendungen ist auf jedenfall Performance, da die
Anfragen schneller sind und das bei sehr großen Datenmengen ein durchaus entscheidender Faktor sein kann. Weiterhin ist der Aufwand, der benötigt wird um SQL Queries zu
entwickeln, normalerweise auch überschaubar und nicht allzu groß.
Allerdings ist die ständige Entwicklung von monotonen Abfragen nicht besonders effizient
und es ist durchaus möglich eine höhere Produktivität zu erzielen, wenn der Fokus auf die
eigentliche Logik und nicht die relationalen Datenabfragen gelegt werden kann.
Vergleicht man nun die unterschiedlichen Objekt relationalen Mapping Lösungen in der
.NET Welt dann kann man sicher sagen dass seit .NET 4.0 und der Möglichkeit LINQ2Entities
eine sehr schöne und schnelle Möglichkeit geschaffen wurde, welche die Vorteile von
LINQ und dem ADO.NET Entity Framework kombiniert.
Ebenfalls ist ein großer Nachteil von LINQ2SQL, der zwingenden Anforderung einen MS
SQL Server zu verwenden, durch die Kombination beider Komponenten leicht lösbar.
Die Entwicklung wird dank der leicht bedienbaren Assistenten des Visual Studios erleichtert und für einen Großteil der Anwendungen wird das bereits ausreichend sein und es ist
nicht zwingend nötig sich in Mapping Details einzuarbeiten.
Die graphischen Werkzeuge sind im Vergleich zu NHibernate auch ein großer Vorteil, da
man bei der Open Source Lösung ähnliche Werkzeuge bisher vermisst.
14
Zusammenfassung
Mit ADO.NET und dem ADO.NET Entity Framework bietet Microsoft ein breites Spektrum an, um den Datenbankzugriff mit .NET in einer Vielzahl von Anwendungsszenarien
auf eine sehr einfache und effiziente Weise zu realisieren.
• ADO.Net lasst sich mit dem Visual Studio sehr einfach konfigurieren und einsetzen
• Die Ähnlichkeit zu Java oder anderen Programmiersprachen ermöglicht einen schnellen Einstieg
• O/R Mapping für komplexere Anwendungsszenarien wird durch das ADO.NET Entity Framework integriert
Für eine einfache Anwendung wie beispielsweise ein überschaubares Reporting empfiehlt
es sich wahrscheinlich eine Lösung ohne O/R Mapping zu realisieren.
Für eine komplexeres Anwendungsszenario wie eine Konsolidierungslogik bietet sich möglicherweise
ein O/R Mapping Framework an. Entscheidend für die Wahl der Entwicklungsmethode
sind allerdings auch weitere Aspekte wie z.B. Wartung und Wiederverwendbarkeit.
Für weitere Informationen ist das Microsoft Developer Network zu empfehlen.
Literatur
• http://msdn.microsoft.com/de-de/library/bb979090.aspx
• http://msdn.microsoft.com/de-de/library/bb979079.aspx
• http://msdn.microsoft.com/de-de/library/777e5ebh.aspx
• http://msdn.microsoft.com/en-us/library/aa697427.aspx
• http://msdn.microsoft.com/en-us/magazine/cc163399.aspx
• http://msdn.microsoft.com/de-de/library/bb399572.aspx
• http://msdn.microsoft.com/de-de/library/bb399567.aspx
• http://msdn.microsoft.com/library/bb425822.aspx
• http://msdn.microsoft.com/de-de/library/bb399360.aspx
• David Sceppa: ADO.NET 2.0 Entwicklerbuch. Microsoft Press. 2006.
• Wanzke & Wanzke: ADO.NET 2.0 Grundlagen und Profiwissen. Hanser. 2006.
• Martin Fowler: Patterns of enterprise application architecture. Addison-Wesley Longman. 2003.
• http://nhforge.org/wikis/
Herunterladen