communityWir suchen ständig neue Tutorials und Artikel! Habt ihr selbst schonmal einen Artikel verfasst und seid bereit dieses Wissen mit der Community zu teilen? Oder würdet ihr gerne einmal über ein Thema schreiben das euch besonders auf dem Herzen liegt? Dann habt ihr nun die Gelegenheit eure Arbeit zu veröffentlichen und den Ruhm dafür zu ernten. Schreibt uns einfach eine Nachricht mit dem Betreff „Community Articles“ und helft mit das Angebot an guten Artikeln zu vergrößern. Als Autor werdet ihr für den internen Bereich freigeschaltet und könnt dort eurer literarischen Ader freien Lauf lassen.

Datenbankanwendungen mit Microsoft SQL Server Compact Drucken E-Mail
Benutzerbewertung: / 142
SchwachPerfekt 
Samstag, den 12. September 2009 um 22:47 Uhr
Beitragsseiten
Datenbankanwendungen mit Microsoft SQL Server Compact
Datenbankentwurf
LINQ to SQL
Benutzeroberfläche
Alle Seiten

Einführung

In diesem Tutorial werden Sie Elemente von Visual Studio kennenlernen, die Sie für die schnelle Entwicklung von Datenbankanwendungen benötigen. Als Datenbank wird das neue moderne Microsoft SQL Server Compact verwendet, ein kompaktes, relationales Datenbanksystem das sowohl für den Einsatz in tragbaren Geräten, als auch für den Desktopbereich konzipiert wurde. In dem Artikel wird auf die drei Hauptkomponenten DataSet, Datenprovider und die Klassen der Datenbindung aus ADO.NET eingegangen. Mit LINQ to SQL werden Sie Datenbankabfragen direkt in C# erstellen.

Microsoft SQL Server Compact

Bei Microsoft SQL Server Compact handelt sich um ein eingebettetes Datenbanksystem. Das System wird im Gegensatz zu einem eigenständigen SQL Server nicht als Standalone-Software installiert, sondern besteht im Wesentlichen aus einer Assembly, die in die Anwendung eingebaut wird. Durch die Entwicklung als Embedded lässt sich die Applikation direkt in entsprechende Anwendungen integrieren, so dass eine weitere Server-Software nicht benötigt wird. In den letzten Jahren sind diese Datenbankformen sehr bekannt geworden, nicht zuletzt durch das erfolgreiche SQLite. Heute wird SQLite unter anderem in Betriebssystemen für Mobiltelefone, wie Symbian OS oder Android als Datenbanksystem verwendet. Außerdem nutzt Version 3 des Mozilla Firefox Browsers komplett SQLite – beispielsweise für die Bookmarks – nachdem schon die Version 2 SQLite für programminterne Datenbanken benutzt hatte.

Mit Visual Studio 2005 erkannte Microsoft erstmals die große Nachfrage nach einer schlanken, in die Anwendung integrierbaren SQL-Datenbank und veröffentlichte die SQL Server Compact 2005 Edition. Dabei handelt es sich um ein .NET Pendant zu SQLite, entwickelt von Microsoft. SQL CE wird von Microsoft kostenlos angeboten und verfügt über eine native 64-Bit Unterstützung. Microsoft SQL Server Compact steht innerhalb der Entwicklung unter .NET zur Verfügung und hat einige Besonderheiten gegenüber anderen Datenbanken: Die Programmbibliothek ist nur wenige Megabyte (MB) groß. Die Datenbanken können verteilt auf mehrere Dateien, bei Bedarf auch in eine einzelne Datei, gespeichert werden, was den Austausch zwischen verschiedenen Systemen erheblich vereinfacht. Die Datenbanken können bei Bedarf mit 128-Bit verschlüsselt werden.

SQL CE teilt sich einen Großteil der API mit den übrigen Microsoft SQL Server Editionen. Es beinhaltet ADO.NET Provider für den Zugriff auf die ADO.NET Schnittstellen. Darüber hinaus besitzt die Programmbibliothek interne Synchronisierungsmechanismen und unterstützt LINQ. SQL CE läuft innerhalb des Anwendungsprozesses und teilt sich denselben Speicherbereich mit allen anderen SQL CE Instanzen.

Bezugsquelle

Seit der ersten Version von SQL CE hat Microsoft weitere Updates veröffentlicht. Die derzeit aktuelle Version ist Microsoft SQL Server Compact 3.5 SP1. Der Installer kann auf http://www.microsoft.com/sql/default.mspx bezogen werden. Laden Sie sich das entsprechende Paket herunter und installieren Sie SQL CE auf Ihrem Computer.

Der Installer installiert lediglich die notwendigen Programmbibliotheken unter dem angegebenen Verzeichnis und registriert diese in Visual Studio.

sql_install

Bei Bedarf lässt sich gleichzeitig auch die Dokumentation mitsamt Beispielen in einem separaten Paket auf der Seite von Microsoft herunterladen.

Visual Studio

Zum Schreiben von .NET-Anwendungen ist es nicht zwingend notwendig, das Visual Studio von Microsoft zu verwenden, bei größeren Anwendungen - und dazu gehören Datenbankanwendungen meistens - erhöht es allerdings die Produktivität deutlich. In diesem Artikel wollen wir daher auf die datenbankspezifischen Fähigkeiten von Visual Studio eingehen.

Neues Projekt starten

  • Starten Sie Visual Studio 2008 durch Klick auf Start | Alle Programme | Microsoft Visual Studio 2008 | Microsoft Visual Studio 2008.
  • Klicken Sie Datei | Neu | Projekt um eine neue Windows Forms Applikation zu kreieren.
  • Wählen Sie in dem Dialogfenster unter Projekttypen Visual C# | Windows.
  • Klicken Sie nun auf Windows Forms-Anwendung in dem Fenster Vorlagen.
  • Ändern Sie den Namen in Shop, wie in der Abbildung unten gezeigt und klicken Sie auf OK.

vs_new_project

SQL Server Compact Edition Datenbank als Datenquelle

Bevor Sie über den Server-Explorer mit einer Datenbank arbeiten können, müssen Sie sich mit der betreffenden Datenbank verbinden. Um eine neue Verbindung zu einer Datenbank hinzuzufügen, können Sie direkt die Werkzeugliste des Server-Explorers nutzen, oder explizit eine Datenquelle angeben.

  • In Visual Studio, klicken Sie Daten | Datenquellen anzeigen um das Datenquellen Fenster anzuzeigen.
  • Innerhalb des Fenster klicken Sie auf "Neue Datenquelle hinzufügen...". Es erscheint der Assistent zum Konfigurieren von Datenquellen.

vs_datasource_windowDer Assistent zum Konfigurieren von Datenquellen in Visual Studio erlaubt es neue Datenquellen zum Projekt hinzuzufügen. Sie können verschiedene Datenquellen angeben, darunter eine SQL Server- oder eine SQL Server Compact-Datenbankdatei, Access, Webservices oder Speicherobjekte. Der Vorgabewert für die Datenquelle ist die SQL Server-Datenbankdatei. Diese Art der Verbindung ist neu für den SQL Server 2005 und erlaubt es, direkt eine Datei als Datenbank zu verwenden, ohne diese erst mehr oder weniger kompliziert einzurichten. Die Verwendung der SQL Server-Datenbankdatei ist ähnlich einfach wie die Verwendung einer Access-Datei.

Microsoft SQL Server Compact Edition wird vom ADO.NET SqlDataProvider unterstützt. Es ist sogar möglich eine Oracle-Datenbank auszuwählen, da ein OracleDataProvider jetzt standardmäßig mit .NET ausgeliefert wird. Neu ist, dass Sie den DataProvider auswählen können. In älteren Versionen von Visual Studio wurde immer der OleDbDataProvider verwendet, jetzt können Sie auch den SqlDataProvider auswählen. Der DataProvider ist wichtig, da von diesem die Funktionen abhängen, die Ihnen im Server-Explorer zur Verfügung stehen, z.B. um Tabellen anzulegen oder deren Spalten zu editieren.

Für die Beispielanwendung in diesem Artikel werden wir eine SQL Server Compact Edition-Datenbankdatei generieren und anschließend eine eigene Datenbankstruktur erzeugen.

Nachdem Sie auf "Neue Datenquelle hinzufügen..." geklickt haben, können Sie im Assistenten den Datenquelltyp auswählen. Abhängig vom Datenquelltyp wird ein DataSet erstellt. Es handelt sich dabei um ein Speicherabbild der eigentlichen Datenbank. Ein DataSet wird immer dann eingesetzt, wenn Daten mehrmals benötigt und von der Anwendung oft geändert werden. In diesem Fall werden die Daten über den DataAdapter im DataSet gespeichert, wo sie der Anwendung zur weiteren Verwendung zur Verfügung stehen. Das DataSet wird automatisch mit den Objekten der Datenbank gefüllt.

Wählen Sie nun als Datenquelltyp die Datenbank aus und klicken Sie auf Weiter.

assistent_zum konfigurieren_von datenquellen

Der Assistent fordert Sie nun auf die Datenverbindung zu wählen. Klicken Sie auf Neue Verbindung um das Dialogfenster "Verbindung hinzufügen" zu öffnen.

datenverbindung_waehlen

In dem Dialogfeld klicken Sie auf Ändern, um die Datenquelle zu ändern.

verbindung_hinzufuegen

In diesem Dialogfeld können Sie nun die Datenquelle auswählen. Wählen Sie Microsoft SQL Server Compact und klicken Sie auf OK.

datenquelle_wechseln

Wir möchten in diesem Artikel eine eigene Datenbank erstellen. Klicken Sie deshalb auf den Button Erstellen. In dem neuen Dialogfenster können Sie nun den Namen der Datenbankdatei angeben. Die Dateiendung einer SQL Server Compact-Datenbankdatei lautet *.sdf. Wir nennen unsere Datenbankdatei shop.sdf. In dem Dialogfenster können diverse Datenbankeigenschaften ausgewählt werden. Zusätzlich kann die Datenbank verschlüsselt werden, so dass nur mithilfe eines Passwortes darauf zugegriffen werden kann. Wir lassen in unserem Beispiel das Kennwortfeld leer und bestätigen zweimal mit OK.

sql_ce_datenbank_erstellen

Sie können anschließend die neu erstellte Datenbank testen, indem Sie eine Verbindung herstellen. Nachdem nun wieder der Assistent zum Konfigurieren von Datenquellen zu sehen ist - mit der erstellten Datenbank in der Auswahlliste - kann der Button Weiter betätigt werden. Visual Studio weist Sie darauf hin, dass es sich um eine Verbindung zu einer lokalen Datendatei handelt und fragt ob eine Kopie erstellt werden soll. Bestätigen Sie das mit OK.

Nun wird die Verbindungszeichenfolge angezeigt. Belassen Sie den Namen und klicken auf Weiter.

verbindungszeichenfolge_in_der_anwendungskonfigurationsdatei_speichern

Abschließend können Sie den Vorgang beenden. Der Assistent weist Sie an dieser Stelle darauf hin das die Datenbank keine Objekte enthält, folglich werden im Fenster auch keine Objekte gelistet. Der Name des DataSet sollte auch nicht verändert werden.

datenbankobjekte_auswaehlen

Klicken Sie nun auf Fertig stellen. Visual Studio beginnt nun die erforderlichen Dateien zu erstellen und zum Projekt hinzuzufügen.

shop_projektmappen_explorerSie sollten nun im Projektmappen-Explorer einige Veränderungen beobachten können. Visual Studio hat für Sie automatisch einen Verweis auf die notwendigen Assemblys hinzugefügt und die erstellte Datenbank eingebunden. Gleichzeitig wurde ein leeres DataSet erstellt. Damit ist die Konfiguration der Datenbankverbindung abgeschlossen.

Grundlagen zu ADO.NET

ADO.NET ist ein Teil der von Microsoft entwickelten .NET-Plattform. Es handelt sich um eine Sammlung von Klassen, die den Zugriff auf relationale Datenbanken gewährleisten.

ADO.NET gilt als Nachfolger der ActiveX Data Objects (ADO), ist aber um zahlreiche Funktionen erweitert worden, so dass man von einer Neuentwicklung sprechen kann.

Das Hauptanliegen von ADO.NET besteht darin, die Datenbeschaffung von der Bereitstellung und Anzeige der Daten vollständig zu trennen. Um dieses Ziel zu erreichen, spaltet sich ADO.NET in die drei Hauptkomponenten DataSet, Datenprovider und die Klassen der Datenbindung auf. Letztere stellen allerdings keinen integralen Bestandteil von ADO.NET dar, stattdessen dienen sie der Anbindung der Steuerelemente an ein DataSet. ADO.NET kann als Revolution bei den Datenzugriffstechnologien angesehen werden. Es ermöglicht einen einheitlichen Ansatz, um auf Daten unterschiedlicher Datenquellen zuzugreifen, wie z.B. SQL Server, Oracle, Access und SQL Server Compact.

Die Klassen von ADO.NET befinden sich im Namespace „System.Data“. Für die Datenprovider gibt es weitere Namespaces, wie z.B. „System.Data.SqlClient“ oder bei SQL CE „System.Data.SqlServerCe“. Die Datenbindung ist Bestandteil von „Windows.Forms“, da sie nur in den Benutzeroberflächen verwendet werden. Im Namespace „System.Xml“ befinden sich ebenfalls einige Klassen, die für die Zusammenarbeit mit dem DataSet zuständig sind.

architecture_of_ado.net

ADO.NET stellt prinzipiell zwei verschiedene Wege zur Verfügung, an Daten aus einer Datenquelle heranzukommen. Dieser führt entweder über den DataReader oder über das DataSet.

DataSet

Ein DataSet ist eine diskonnektierte, relationale Datenbank im RAM des Computers. Es handelt sich um ein vollständiges Abbild der persistenten Datenbank im flüchtigen Arbeitsspeicher. Ein DataSet wird immer dann eingesetzt, wenn Daten mehrmals benötigt und von der Anwendung oft geändert werden. In diesem Fall werden die Daten über den DataAdapter im DataSet gespeichert, wo sie der Anwendung zur weiteren Verwendung zur Verfügung stehen. Das DataSet verfügt über Methoden, um die gespeicherten Daten im XML-Format in eine Datei oder einen Stream zu schreiben oder von dort zu lesen.

Konzeptionell bedingt erzeugt das DataSet im Vergleich zur direkten Verwendung des DataReaders einen gewissen Overhead. Das betrifft die Speicherauslastung, als auch die Zugriffsgeschwindigkeit. Die Zugriffsgeschwindigkeit ist allerdings vernachlässigbar, so dass die Vorteile des DataSets meist deutlich überwiegen.

Datenprovider

Der Datenprovider ist die Schnittstelle zu einer Datenbank. Er muss fachliche Informationen über die Datenbank besitzen, d.h. er muss die Datenbank kennen. Für unterschiedliche Datenbanken existieren individuelle Datenprovider. Im .NET Framework sind die Datenprovider SQL Server und OLE DB standardmäßig enthalten. Auch für viele Open-Source-Datenbanken, wie z.B. MySQL, existieren .NET-Datenprovider.

Die vier Kernkomponenten der .NET-Datenprovider sind:

  • Connection: Stellt eine Verbindung her, die der Kommunikation mit einer Datenquelle dient. Seit .NET 2.0 ist es möglich bei der Verbindung umfangreiche Metadaten zur Datenbank zu laden.
  • Command: Führt Anweisungen, gespeicherte Prozeduren und Aktionsabfragen aus. Dazu gehören unter anderem SELECT-, UPDATE- oder DELETE-Kommandos.
  • DataAdapter: Der DataAdapter füllt ein DataSet mit Daten und gleicht Aktualisierungen mit der Datenquelle ab. Er fungiert als Brücke zwischen der Datenquelle und einem DataSet-Objekt.
  • DataReader: Es handelt sich um einen vorwärts gerichteten Datensatzleser, der nur einen lesenden Zugriff auf die Daten gestattet. Eine Navigation durch die Datensätze ist dabei nicht möglich, da diese sequentiell abgearbeitet werden.

Datenbankentwurf

Eine Datenbank ohne Objekte ist nicht sehr nützlich, schließlich ist die wesentliche Aufgabe eines DBS, große Datenmengen effizient, widerspruchsfrei und dauerhaft zu speichern und benötigte Teilmengen in unterschiedlichen, bedarfsgerechten Darstellungsformen für Benutzer und Anwendungsprogramme bereitzustellen. Der Datenbankentwurf ist der Prozess der Umsetzung einer "Mini-Welt" in ein Datenbankschema, welches dann in der Lage ist, diese Welt mit ihren Eigenschaften und Beziehungen zu simulieren.

ER-Modell

Um eine Datenbank zu entwerfen, bedient man sich häufig des Entity-Relationship-Modells (ER-Modell). Mit seiner Hilfe wird das Datenmodell entwickelt. Das ER-Modell geht dabei von Objekten des abzubildenden Relitätsausschnitts aus. Das Entity-Relationship-Modell (dt. Gegenstands-Beziehungs-Modell), dient dazu, im Rahmen der Datenmodellierung einen Ausschnitt der realen Welt zu beschreiben. Es besteht meistens aus einer Grafik und einer Beschreibung der darin verwendeten einzelnen Elemente.

Es dient zum einen in der konzeptionellen Phase der Anwendungsentwicklung der Verständigung zwischen Anwendern und Entwicklern, wobei ausschließlich das Was, also die Sachlogik, und nicht das Wie, also die Technik, wesentlich ist. Zum Anderen dient das ER-Modell in der Implementierungsphase als Grundlage für das Design der Datenbank. Ein ER-Modell besteht aus den Elementen Entity (Gegenstandstyp), Relationship (Beziehungstyp), Attribut (Eigenschaft), Schlüssel (Identifikation) und Rolle.

Wir beginnen deshalb diese Objekte zunächst zu benennen. Unsere Datenbankanwendung soll ein Shop (dt. Laden) werden. Ein Shop verfügt in aller Regel über Artikel. Diese Artikel haben einen Hersteller. Kunden können Artikel in Ihrem Shop bestellen. Aus diesen Informationen lässt sich nun das ER-Modell entwerfen.

shop_er_diagramm

Die blauen Elemente im ER-Diagramm, die Attribute, wurden entsprechend der Objekte sinnvoll gewählt. So besitzt ein Artikel eine Artikelnummer, eine Bezeichnung und einen Preis.

Relationales Datenmodell

Um mit den Inhalten einer Datenbank arbeiten zu können, müssen diese zuvor im Rahmen des Datenmodells beschrieben werden. Das Datenmodell legt folgende Informationen fest:

  • die Eigenschaften der Datenelemente
  • die Struktur der Datenelemente
  • die Abhängigkeiten von Datenelementen, die zu Konsistenzbedingungen führen
  • die Regeln zum Speichern, Auffinden, Ändern und Löschen von Datenelementen

In der Praxis wird das relationale Datenmodell zurzeit am häufigsten verwendet. Im relationalen Datenmodell wird die Beziehung der Sachverhalte durch das (mathematische) Konzept der Relationen dargestellt. Eine Relation besteht aus Attributen, die die Objekte mit einem Bezeichner und einem Wert beschreiben.

Die Menge aller Attribute wird als Tupel bezeichnet, das im Beispiel so aussehen kann:

t = [Name = 'Schmidt', Vorname = 'Hans', Ort = 'München']

Die Menge aller Tupel mit gleichen Attributen wird als Relation bezeichnet. Im relationalen Datenmodell werden zur Darstellung bzw. Behandlung ebenfalls Schlüssel benötigt. Über den Primärschlüssel werden Datensätze eindeutig identifiziert, Fremdschlüssel dienen zur Beschreibung der Beziehungen zwischen verschiedenen Relationen.

Optimierung des Datenmodells (Normalisierung)

Die Definierung des Datenmodells ist insbesondere bei komplexen Problemlösungen nicht einfach. Um die Erstellung des Datenmodells zu vereinfachen und Fehler z.B. in Form von Redundanzen zu vermeiden, wurden Regeln entwickelt, nach denen das Datenmodell effektiv erstellt werden kann. Diese Behandlung wird als Normalisierung bezeichnet. Die Normalisierung erfolgt dabei in mehreren Schritten.

Grundlage für die Normalisierung ist eine Tabelle bzw. das ER-Modell. Aus dem Entity-Relationship-Modell lässt sich die gewünschte Normalform erstellen. Die Details der Normierung sollen in diesem Artikel nicht weiter erörtert werden und können jedem guten Buch über Datenbanken entnommen werden.

Das Ergebnis der Normalisierung unseres ER-Modells ist die 3. Normalform mit den folgenden Tabellen:

Artikel
Artikelnr Bezeichnung Preis Herstellernr
       

Kunden
Kundennr Name Vorname PLZ Ort Strasse Hausnr
             

Hersteller
Herstellernr Name Telefonnr
     

Bestellungen
Bestellnr Kundennr Bestelldatum Status
       

Enthaelt
Id Bestellnr Artikelnr Menge
       

Mit der 3. Normalform ist der Datenbankentwurf in unserer trivialen Anwendung abgeschlossen. Nun kann mit der Implementierung der SQL CE Datenbank in Visual Studio begonnen werden.

Arbeiten mit Tabellen in Visual Studio

Während der Entwicklung müssen häufig neue Tabellen der Datenbank hinzugefügt, bestehende um neue Felder ergänzt oder Eigenschaften verändert werden. Wenn Sie mit SQL Server- oder SQL Server Compact-Datenbanken arbeiten, können Sie diese Aufgaben innerhalb der Entwicklungsumgebung über den Server-Explorer erledigen.

Bearbeiten von Tabellen über das Datenbankdiagramm

Wenn Sie viele Tabellen ändern oder Beziehungen zwischen Tabellen pflegen wollen, erledigen Sie das am einfachsten über ein Datenbankdiagramm. Leider unterstützt Visual Studio 2008 in Verbindung mit Microsoft SQL Server Compact keine Datenbankdiagramme. Diese werden in vollem Umfang nur von SQL Server-Datenbankdateien (.mdf) unterstützt. Sofern Sie auf SQL CE zurückgreifen, werden Sie beim erstmaligen Erstellen der Datenbankstruktur manuell vorgehen müssen und die Tabellen Schritt für Schritt erstellen. Wir werden später sehen, dass es eine Möglichkeit gibt ein Datenbankdiagramm auch für eine SQL CE-Datenbank erzeugen zu lassen.

server_explorerVisual Studio 2008 bietet einige Optionen bei der Bearbeitung von Tabellen in einer Microsoft SQL Server Compact-Datenbank an, die zwar nicht so vielfältig und optisch ansprechend sind, wie die Optionen in einem Datenbankdiagramm, die Arbeit dennoch sehr erleichtern. Neue Tabellen werden in SQL mit CREATE erstellt und mit ALTER werden Tabellendefinitionen verändert. Diese SQL-Befehle können direkt verwendet werden, um die Datenbankstruktur zu generieren. In Visual Studio 2008 stehen hierfür diverse Menüs zur Verfügung, die eine Bearbeitung über eine grafische Benutzeroberfläche ermöglichen.

Klicken Sie im Server-Explorer auf die SQL CE-Datenbankdatei und öffnen Sie mit der rechten Maustaste den Baum. Sie finden zwei Knoten, Tabellen und Replikation.

Erstellen Sie nun mit dem Menüpunkt Tabelle erstellen eine neue Tabelle. Es öffnet sich ein Dialogfenster mit verschiedenen Optionen. In der Zeile ganz oben kann der Name der Tabelle angegeben werden. Weiter unten findet sich eine Liste, die die Spalten der Tabelle repräsentiert.

Wir beginnen mit der Erstellung der ersten Tabelle, nämlich „Kunden“. Geben Sie dazu den Namen in die entsprechende Zeile ein. Kicken Sie nun mit einem Doppelklick in die Liste auf das erste freie Feld unter Spaltenname. Sie müssten nun einen Namen eingeben können. Die erste Spalte in der Tabelle „Kunden“ lautet Kundennr. Nach der Eingabe des Spaltennamens füllt Visual Studio für Sie automatisch die anderen Spalten in der Liste mit Standardwerten.

Jede Spalte in einer Tabelle hat einen bestimmten Typ, z.B. Integer oder String. Bei einer Spalte kann es sich um einen Primärschlüssel handeln. Im relationalen Datenmodell spielt die Verknüpfung von verschiedenen Tabellen eine entscheidende Rolle. Um Tabellen eindeutig verknüpfen zu können, muss allerdings jeder Datensatz einer Tabelle eindeutig identifiziert und adressiert werden können. Ein Attribut, das einen Datensatz mit allen seinen Feldwerten eindeutig identifiziert, wird als Primärschlüssel bezeichnet.

Ein Primärschlüssel darf nicht leer sein, so dass Visual Studio bei der Auswahl automatisch die Spalte »Null-Wert« auf „Nein“ setzt, sobald die Spalte als Primärschlüssel definiert wurde. Jede Tabelle darf nur genau einen Primärschlüssel haben und es dürfen keine Duplikate in den Datensätzen derselben Tabelle existieren.

Zeilenidentität und Primärschlüssel

Die Zeilenidentität für eine Tabelle wird durch die Vergabe eines Primärschlüssels für diese Tabelle eingeführt. Der Primärschlüssel besteht letztlich aus einer Aneinanderreihung von Spalten, die zusammengenommen eine Tabellenzeile als eindeutig kennzeichnen. Zum Beispiel könnte in einer Tabelle mit Personendaten die Kombination von Nachname und Vorname als Primärschlüssel definiert werden - vorausgesetzt, es gibt keine Personen mit gleichen Nach- und Vornamen.

Wählt man in Visual Studio 2008 eine Spalte aus und deklariert sie als Primärschlüssel, wird diese Spalte nicht unmittelbar zu einer Identitätsspalte.

Der Unterschied zwischen einem Primärschlüssel mit Identität und einem Primärschlüssel ohne Identität ist, dass bei einer Identitätsspalte die Funktion Auto-Increment aktiv ist und Sie die Spalte nicht selbst editieren können. Beim Einfügen von Datensätzen wird dieser Spalte automatisch ein eindeutiger Wert zugewiesen.

identitaetsspalteDie Wahl eines geeigneten Primärschlüssels für eine Tabelle ist eine wichtige Aufgabe beim Design einer Datenbank. Wesentlich ist vor allem, dass ein Primärschlüssel gefunden wird, dessen Spalten später nicht aktualisiert werden müssen. Eine solche Aktualisierung bringt stets große Probleme mit sich, wie Sie sich sicherlich am Beispiel mit den Personendaten ausmalen können.

In unserer Kundentabelle soll die Kundennummer einzigartig pro Kunde sein und so wählen wir für die Spalte eine Zahl aus, nämlich den Datentyp „int“. Es handelt sich bei der Spalte um einen eindeutigen Primärschlüssel. Weisen Sie unten für die Spalte der Identität True zu. Doppelklicken Sie nun auf die nächste freie Zeile und benennen Sie diese mit Name. Der Name ist vom Typ „nvarchar“ (engl. national character varying), maximal 100 Zeichen lang und darf nicht NULL sein. Dasselbe trifft auf die nächste Spalte der Tabelle „Kunden“ zu, nämlich Vorname. Die Spalte PLZ ist vom Typ „int“ und darf ebenfalls nicht NULL sein. Der Ort ist ein „nvarchar“ der Länge 100. Die Strasse ebenfalls. Die letzte Spalte trägt den Datentyp „smallint“ mit einer Länge von 2 Byte.

Nachdem Sie die erforderlichen Einstellungen getätigt haben, können Sie das Fenster mit OK schließen.

visual_studio_neue_tabelle

Sie werden nun im Server-Explorer die neue Tabelle „Kunden“ erblicken können. Erstellen Sie auf dieselbe Art und Weise die restlichen Tabellen „Artikel“, „Bestellungen“, „Hersteller“ und „Enthaelt“. Die unterstrichenen Spaltennamen in den gezeigten Tabellen stellen immer den Primärschlüssel dar. Wählen Sie für die Felder sinnvolle Datentypen aus und achten Sie darauf bei wichtigen Spalten NULL-Werte zu unterbinden. Sollten Sie Schwierigkeiten bei der Erstellung haben oder diesen Schritt überspringen wollen, können Sie die fertige Datenbank am Ende dieses Tutorials verwenden.

Fremdschlüssel und referenzielle Integrität

Microsoft SQL Server Compact ist nicht nur ein kompaktes, sondern auch ein relationales Datenbanksystem. Grundlage des Konzeptes relationaler Datenbanken ist die Relation, ein im mathematischen Sinn wohldefinierter Begriff. Die eigentliche Verknüpfung zwischen Relationen erfolgt über Werte, die als Fremdschlüssel bzw. Foreign Keys bezeichnet werden. Ein Fremdschlüssel ist ein Attribut, das sich auf einen Wert des Primärschlüssels einer anderen (oder gleichen) Relation bezieht.

Für die Beziehungen über Fremdschlüssel wird in der Regel gefordert, dass für einen Wert des Fremdschlüssels immer auch ein Wert in der Masterrelation besteht. Diese Forderung wird als referenzielle Integrität bezeichnet.

Fremdschlüssel lassen sich in Visual Studio im Dialogfenster Tabelleneigenschaften hinzufügen und verwalten. Klicken Sie dazu auf eine Tabelle und wählen sie den entsprechenden Menüpunkt aus. Die Tabelle „Artikel“ besitzt einen Fremdschlüssel, nämlich Herstellernr. Diese Spalte ist der Primärschlüssel der Tabelle „Hersteller“. Beide Tabellen stehen also in Beziehung zueinander und diese Beziehung gilt es zu kennzeichnen.

visual_studio_tabelleigenschaften

Die Fremdschlüssel sind in den dargestellten Tabellen kursiv dargestellt. Erstellen Sie mit dem oben demonstrierten Verfahren alle übrigen Verknüpfungen.

Verwenden eines DataSets als Datenquelle

DataSets erleichtern den Umgang mit Daten und so stellt Visual Studio ausgefeilte Methoden bereit, um ein DataSet aus einer bestehenden Datenbank zu generieren.

DataSet erstellen

Nachdem die Tabellenstruktur erfolgreich erstellt wurde und alle Beziehungen angegeben wurden, kann ein DataSet erstellt werden. Visual Studio bietet einen bequemen Weg ein Abbild der Datenbank in ein DataSet zu konvertieren. Klicken Sie dazu einfach im Server-Explorer auf eine Tabelle und ziehen Sie diese Tabelle in das zuvor geöffnete DataSet. Beginnen Sie mit der Tabelle „Kunden“.

shop_dataset

Visual Studio zeigt Ihnen nun auch die Fremdschlüsselbeziehungen zwischen den Tabellen in dem Diagramm an.

datenquellenWenn Sie sich jetzt das Projekt anschauen, können Sie an drei Stellen Änderungen feststellen, nämlich an dem Datenquellenfenster, der Anwendungskonfiguration und dem Projektmappen-Explorer.

Das Datenquellenfenster enthält jetzt ein neues typisiertes DataSet. Hier können Sie alle Tabellen des DataSets sehen und diese weiter bis auf die Spalten erweitern. Die Tabelle „Enthaelt“ ist zweimal vorhanden, nämlich einmal unterhalb der Tabelle „Artikel“ und einmal als eigenständige Tabelle des DataSets. Die Tabelle unterhalb von „Artikel“ enthält die relationale Beziehung zwischen diesen Tabellen, nämlich alle in einer Bestellung enthaltenen Artikel. Die Beziehung wurde innerhalb der Microsoft SQL Server Compact-Datenbank automatisch vom DataSet-Generator berücksichtigt.

Über den Projektmappen-Explorer können Sie Einstellungen öffnen. Wenn Sie dies tun, finden Sie die Einstellung für den ConnectionString. Alle Einstellungen werden im Projektverzeichnis als XML-Daten in der app.config gespeichert. Wenn Sie das Projekt kompilieren, werden die Einstellungen in eine Datei assemblyname.exe.config in das Zielverzeichnis übertragen. Sie können dann, ohne das Projekt neu kompilieren zu müssen, in dieser Datei die Einstellungen ändern. Diese werden dann beim nächsten Auslesen, z.B. beim Neustart der Anwendung, übernommen. Die XML-Datei ist nicht ganz so übersichtlich wie das Editieren innerhalb von Visual Studio, besonders dann, wenn noch mehrere Einstellungen hinzukommen, aber es ist zumindest möglich. Gerade bei ConnectionStrings ist das notwendig, da man innerhalb der Entwicklungsumgebung in der Regel mit einer anderen Datenbank arbeitet als später in der produktiven Anwendung.

Unter den Properties ist die Datei Settings.setting zu finden. Diese können Sie im Editor öffnen und diverse Einstellungen bei der Datenbankverbindung vornehmen. In der Dropdownliste können Sie einen Typ für die Einstellung auswählen. Die am häufigsten verwendeten Typen werden in der Dropdownliste aufgeführt, z. B. Zeichenfolge, (Verbindungszeichenfolge). Eine Verbindungszeichenfolge beschreibt den Standort einer Datenbank und die Zugangsweg zu dieser Datenbank.

Das vom Designer automatisch generierte DataSet ist ein exaktes Abbild des Datenbankschemas. Sobald Sie das Schema in der Datenbank ändern, müssen sie auch das DataSet entsprechend korrigieren.


DataSet in Formular einbinden

Nachdem die Datenquelle angelegt ist, werden wir diese nun im Formular verwenden. In Visual Studio lässt sich eine Steuerung direkt mit jeder Spalte aus der Datenbank verknüpfen, indem die entsprechende Spalte aus der Tabelle oder die gesamte Tabelle selbst auf das Formular gezogen wird. Auf diese Weise können Sie den Benutzer über die GUI direkt die Datenbank manipulieren lassen.

Es gibt prinzipiell zwei Varianten eine Tabelle auf ein Formular zu bringen. Sie können die Tabelle direkt einfügen, diese wird dann vollständig als DataGridView angezeigt, oder Sie klicken die Tabelle an und wählen in der Dropdownliste die Option Details. In diesem Fall wird für jede Spalte eine individuelle Control generiert. Wir wollen ein Formular erstellen, in dem der Benutzer neue Kunden in die Datenbank aufnehmen kann.

  • Öffnen Sie das Formular im Designmodus. Das Formular ist jetzt noch vollständig leer.
  • Öffnen Sie das Datenquellenfenster.
  • Klicken Sie nun in der Dropdownliste der Tabelle „Kunden“ die Option Details an.
  • Ziehen Sie die Tabelle „Kunden“ aus dem Datenquellenfenster in das Formular.

Die Entwicklungsumgebung hat für Sie nun alle Steuerelemente erstellt und eine Navigationsleiste eingefügt. Mit der Navigationsleiste können Sie über die Einträge in der Datenbank iterieren, neue Einträge erstellen, diese löschen und abspeichern. Der BindingNavigator wird als Objekt unter dem Formular angezeigt. Gleichzeitig wurden weitere Elemente an die Datenquelle gebunden.

kundenformular
Datenbindung

Die Datenbindung verbindet eine Datenquelle, z.B. eine DataTable, mit einem Datenverbraucher, z.B. die DataGridView oder eine einfache Textbox. Über die Datenbindung werden die Daten zwischen der Datenquelle und dem Datenverbraucher in beiden Richtungen ausgetauscht. Der Datenfluss wird durch mehrere Klassen geregelt. Auf diesen Datenfluss können Sie über Klasseneigenschaften und auch über Ereignisse Einfluss nehmen und z.B. Formatierungen vorgeben.

Die einfache Datenbindung bezeichnet die Bindung einer Eigenschaft eines Steuerelements an ein einziges Datenelement (eine Spalte in einem Datensatz). In der Regel können mehrere Eigenschaften eines Steuerlements an ein Datenelement gebunden werden. Für eine Textbox oder ein Label wird in der Regel die Text-Eigenschaft angebunden. Es ist aber auch möglich, z.B. die Readonly-Eigenschaft der Textbox an eine Spalte anzubinden, die einen boolschen Wert besitzt.

Bei der komplexen Bindung werden mehrere Datensätze gebunden. Die DataGridView und die Listbox sind zwei Beispiele für Steuerelemente, die eine komplexe Bindung unterstützen.

Datensätze einfügen

Die Tabelle „Kunden“ besitzt keine Fremdschlüsselabhängigkeit, so dass problemlos Datensätze eingefügt werden können. Starten Sie nun mit F5 den Kompiliervorgang und führen Sie die Anwendung aus. Sie müssten nun das erstellte Formular sehen können. Fügen Sie Beispieldaten ein und speichern sie diese oben in der Navigationsleiste mit dem Diskettensymbol ab. Die Anwendung speichert die Daten nun automatisch in der SQL CE-Datenbank. Geben Sie weitere Datensätze an, indem Sie das Plussymbol anklicken und einen neuen Datensatz eingeben. Speichern Sie anschließend erneut ab. Sie können nun über die Navigationsleiste zwischen den erstellten Datensätzen hin und her wechseln.

Wenn Sie die Anwendung beenden und neu starten, werden Sie sehen das alle Datensätze existent bleiben. Sie wurden in der Datenbank shop.sdf hinterlegt. Damit haben Sie ihre erste kleine Datenbankanwendung erfolgreich entwickelt.

Nun ist die bisherige Anwendung bereits brauchbar, wenn es darum geht Datensätze persistent abzuspeichern, aber für einen Shop fehlen noch einige elementare Dinge. So sollte der Nutzer bequem neue Bestellungen aufnehmen können, diese bei Bedarf einsehen und auch sortieren ohne sich durch die Datenbanktabellen hangeln zu müssen. Wir möchten deshalb nun einige nützliche Formulare schreiben, die es dem Nutzer ermöglichen mit den Daten sinnvoll zu arbeiten. Die Grundlage der Bearbeitung von Datensätzen in Datenbanken ist die SQL. Neben dieser klassischen Methode über die ADO.NET-Klassen (Connection, Command und DataReader) gibt es seit .NET 3.5 die Möglichkeit LINQ zu nutzen. Im nächsten Kapitel erfahren Sie dazu mehr.

LINQ

LINQ (Abkürzung für Language INtegrated Query) ist eine Komponente von Microsofts .NET-Framework zur Abfrage von Datenquellen wie Datenbanken oder XML-Dateien. Besonderheit ist, dass SQL-, XLink- und XQuery-Anfragen direkt in .NET-Programmiersprachen wie C# 3.0 oder VB.Net 9.0 als Code statt als String eingebunden werden können. LINQ wurde federführend von Turbo-Pascal- und C#-Autor Anders Hejlsberg entwickelt.

Der Vorteil von LINQ besteht darin, dass der Code durch den Compiler auf Fehler geprüft und optimiert werden kann. Die Syntax von LINQ ist dabei an den Befehlen der SQL-Anfragesprache wie „select“, „from“ und „where“ angelehnt. Die Befehle werden jedoch nicht in SQL-Code umgewandelt, sondern sprechen, mit Hilfe der im .NET-Framework 3.5 eingeführten Erweiterungen, direkt .NET-Objekte an, welche die gewünschte Operation ausführen. Diese Objekte müssen die Schnittstelle IEnumerable implementieren.

Mehr über LINQ erfahren Sie auf unserer Homepage in dem Artikel „C# und LINQ“.

LINQ to SQL

In LINQ to SQL wird eine Linq-Abfrage zu einem SQL-Statement umgewandelt. Dieses Statement ist für MS SQL Server ausgelegt. Da SQL Server die Daten relational speichert und Linq mit Datenkapselung arbeitet, müssen die Objekte „gemappt“ werden.

Visual Studio 2008 unterstützt LINQ to SQL in Verbindung mit SQL Server Compact nicht. Allerdings wird es vom .NET-Framework unterstützt, so dass es problemlos möglich ist LINQ zu verwenden. Es gibt sogar einen Trick, wie man Visual Studio 2008 dazu bewegen kann die erforderlichen Dateien zu generieren. Sie benötigen dazu lediglich das Tool SqlMetal.exe, welches im Windows SDK enthalten ist und die automatische Codegenerierung von Wrapper-Klassen für Microsoft SQL Server-Datenbanken in C# mit DLINQ ermöglicht.

  • Kopieren Sie die sdf-Datenbankdatei in dasselbe Verzeichnis, in dem sich auch SqlMetal.exe befindet.
  • Starten Sie mit Start | Ausführen | Cmd die Konsole als Administrator.
  • Wechseln Sie nun in der Konsole mit cd C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\ in das Verzeichnis von SqlMetal.exe.
  • Führen Sie das Tool mit SqlMetal.exe shop.sdf /dbml:ShopDatabase.dbml aus.
  • Kopieren Sie nun die erzeugte Datei ShopDatabase.dbml in das Projektverzeichnis.
  • Fügen Sie die Datei nun Ihrem Projekt hinzu.

Sobald die Datei *.dbml hinzugefügt wurde, generiert Visual Studio 2008 automatisch den .NET Code (C# oder VB.NET) für die System.Data.Linq.DataContext Klasse, die Sie verwenden werden um auf die Daten zuzugreifen.

namespace Shop
{
    using System.Data.Linq;
    using System.Data.Linq.Mapping;
    using System.Data;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Linq;
    using System.Linq.Expressions;
    using System.ComponentModel;
    using System;
 
 
    [System.Data.Linq.Mapping.DatabaseAttribute(Name="shop")]
    public partial class ShopDatabase : System.Data.Linq.DataContext
    {    
        private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource();
        // ...
 

Achten Sie darauf das es beim Import nicht zu Namenskonflikten kommt. Der Designer generiert automatisch eine partielle Klasse, die den Namen, der in der Datei *.dbml spezifiziert wurde, trägt. Falls ihr Projekt in dem Namensraum Shop eingebettet wurde, sollte die partielle Klasse nicht denselben Namen tragen, da es sonst zu Auflösungsproblemen kommen kann. Der Compiler kann dann nicht mehr zwischen der Klasse und dem Namensraum sauber unterscheiden. In dem Codeabschnitt oben wurde der Name der partiellen Klasse deshalb in ShopDatabase abgeändert. Sie können den Namen auch vor dem Import in der Datei ShopDatabase.dbml anpassen.

Nun können Sie die Datei ShopDatabase.dbml in der Entwicklungsumgebung anklicken. Visual Studio 2008 zeigt Ihnen, wie bei dem DataSet, ein vollständiges Datenbankdiagramm an, diesmal ein Abbild der LINQ Wrapper-Klassen. Sie können das Diagramm anklicken und sich auch die Spalteneigenschaften im Detail anzeigen lassen.

linq_datenbankdiagramm
Beispiele

In einigen kurzen Beispielcodes soll die Funktionsweise von LINQ erläutert werden. Wir möchten den Wohnort einer Kundin mit dem Namen "Vogel" in Hamburg abändern.

string name = "Vogel";
var kunde = db.Kunden.Single(k => k.Name == name);
kunde.Ort = "Hamburg";
 
// Submit the change to the database.
try {
    db.SubmitChanges();
} catch (Exception ex) {
 
}

An dieser Stelle können Sie auch den Einsatz sogenannter Lambda-Ausdrücke sehen. Möchten Sie alle Kunden mit gerader Kundennummer auslesen, so können Sie eine SQL ähnliche Anweisung verwenden.

var q =
   from k in db.Kunden
   where k.Kundennr % 2 == 0
   select k;
 
foreach (Kunden kunden in q) {
    textBox1.Text += kunden.Name + "\r\n";
}

Oder Sie nutzen das Lambda-Kalkül.

var q = db.Kunden.Where(k => k.Kundennr % 2 == 0);

Lassen Sie uns nun alle Vornamen von Kunden ausgeben, die in Berlin wohnhaft sind.

var q = from k in db.Kunden
        where k.Ort == "Berlin"
        select k;
 
foreach (Kunden kunden in q) {
    textBox1.Text = kunden.Vorname  + "\r\n";
}

Ein neuer Kunde kann explizit mit der folgenden Anweisung erstellt werden.

// Create a new Kunden object.
Kunden kunden = new Kunden {
    Kundennr = 5,    // Set only, if column not identity
    Name = "Walter",
    Vorname = "Gustav",
    PLZ = 20654,
    Ort = "Hamburg",
    Strasse = "Elisenstrasse",
    Hausnr = 1
};
 
// Add the new object to the Orders collection.
db.Kunden.InsertOnSubmit(kunden);
 
// Submit the change to the database.
try {
    db.SubmitChanges();
} catch (Exception ex) {
 
}

Einzelne Zeilen in einer Tabelle lassen sich mit Count zählen.

int c = db.Kunden.Count();

Will man die höchste ID eines Primärschlüssels in Erfahrung bringen, so verwendet man Max.

int max = (from k in db.Kunden select k.Kundennr).Max();

Möchte man die Daten sortiert ausgeben, so erreicht man das mit dem Schlüsselwort orderby, welches der SQL-Anweisung ORDER BY entspricht. Gleichzeitig kann mit Take, ähnlich wie in SQL mit LIMIT, die Anzahl der Zeilen eingegrenzt werden.

var q = (from k in db.Kunden orderby k.Vorname select k).Take(2);
 
foreach (Kunden kunden in q) {
    textBox1.Text += kunden.Vorname + "\r\n";
}

Desweiteren ist es möglich Take mit Skip zu kombinieren. Auf diese Weise werden die ersten drei Zeilen übersprungen.

var q = (from k in db.Kunden orderby k.Vorname select k).Skip(3).Take(2);
 
foreach (Kunden kunden in q) {
    textBox1.Text += kunden.Vorname + "\r\n";
}

Die nächste Codezeile wählt alle Kunden mit einer Kundennummer kleiner 50 aus und sortiert das Ergebnis alphabetisch nach den Namen.

var q = db.Kunden.Where(k => k.Kundennr < 50).OrderBy(k => k.Name);

Oftmals sollen neue Collections gebildet werden. Das erreichen Sie mit der folgenden Anweisung.

var q =
    from k in db.Kunden
    select new { k.Vorname, k.Ort };
 
foreach (var v in q) {
    textBox1.Text += v.Ort + " " + v.Vorname + "\r\n";
}

Der Join ist auch in LINQ eine wichtige Anweisung, um Tabellen miteinander zu verknüpfen. Der folgende Code ermittelt alle von Frau Vogel bestellten Artikel und gibt diese sortiert aus.

var q =
   from k in db.Kunden
   join b in db.Bestellungen on k.Kundennr equals b.Kundennr
   join n in db.Enthaelt on b.Bestellnr equals n.Bestellnr
   join a in db.Artikel on n.Artikelnr equals a.Artikelnr
   where k.Name == "Vogel"
   orderby a.Bezeichnung
   select a;
 
foreach (Artikel art in q) {
    textBox1.Text += art.Bezeichnung + "\r\n";
}
 

Ein Group Join lässt sich mit dem Schlüsselwort into realisieren.

var q =
   from k in db.Kunden
   join b in db.Bestellungen on k.Kundennr equals b.Kundennr into sbest
   select new { k, sbest };

Auf den Seiten des Microsoft Developer Network (MSDN) stellt das Softwareunternehmen unter dem Titel "101 LINQ Samples" viele weitere Beispiele zur Verfügung.

Wenn Sie neu in der Welt von LINQ sind, können Sie sich das kostenlose Tool LINQPad herunterladen. Es eignet sich besonders gut zum Erlernen von LINQ to SQL.

LINQ gewährt dem Programmierer einen angenehmen Weg direkt in der Programmiersprache auf Daten in einer Datenbank zuzugreifen, diese zu verändern oder sogar zu löschen. Neben der Handhabung ist auch die Fehlerreduktion sehr hilfreich, da im Gegensatz zu einer SQL-Anweisung (Zeichenfolge) bei LINQ eine Prüfung der Syntax durch den Compiler erfolgt. Dessen ungeachtet wollen wir uns nun auch mit den ADO.NET-Klassen und SQL befassen.

SQL

Die Structured Query Language, kurz SQL, ist eine Datenbanksprache zur Definition, Abfrage und Manipulation von Daten in relationalen Datenbanken. SQL ist von ANSI und ISO standardisiert und wird von fast allen gängigen Datenbanksystemen unterstützt.

Die Syntax von SQL ist relativ einfach aufgebaut und semantisch an die englische Umgangssprache angelehnt. SQL stellt eine Reihe von Befehlen zur Definition von Datenstrukturen nach der relationalen Algebra, zur Manipulation von Datenbeständen (Einfügen, Bearbeiten und Löschen von Datensätzen) und zur Abfrage von Daten zur Verfügung. Durch seine Rolle als Quasi-Standard ist SQL von großer Bedeutung, da eine weitgehende Unabhängigkeit von der benutzten Software erzielt werden kann.

Datenprovider verwenden

Auf Datenbanken in .NET wird mit dem .NET-Datenprovider aus ADO.NET zugegriffen. Für Microsoft SQL Server Compact stehen selbstverständlich ebenfalls Provider zur Verfügung, die Sie auch ohne Umwege ansprechen können. Im Gegensatz zu LINQ werden keine Wrapper-Klassen benötigt. In diesem Kapitel werden Sie mit dem Umgang der wichtigen Klassen Connection, Command, DataReader und DataAdapter vertraut gemacht.

Connection

Die Connection-Klasse (Connection) ist eine relativ einfache Klasse unter den ADO.NET-Klassen. Sie stellt eine Verbindung zu einer Datenbank her und wird bei allen Datenbankzugriffen verwendet. Bei SQL CE heißt die Klasse SqlCeConnection.

Command

Nachdem Sie eine Verbindung zur Datenbank aufgebaut haben, können Sie ein Command-Objekt dazu verwenden, die Daten in der Datenbank zu verändern und/oder auszulesen. Die Command-Klasse kapselt eine Anweisung an die Datenbank. Ein Command repräsentiert eine SQL-Anweisung oder eine gespeicherte Prozedur, die ausgeführt werden soll, und enthält zusätzliche Informationen und Parameter für die Datenbank. Jede Datenbankoperation wird über ein Command ausgeführt.

Die folgende Datenbankabfrage gibt alle von Frau Vogel bestellten Artikel zurück. Die Artikelbezeichnung wird anschließend in einer TextBox ausgegeben.

SqlCeConnection conn = new SqlCeConnection(connString);
SqlCeCommand cmd = null;
SqlCeDataReader rdr = null;
 
try {
 
    conn.Open();
 
    cmd = conn.CreateCommand();
 
    cmd.CommandText = "SELECT * " +
                      "FROM Artikel a " + 
                      "INNER JOIN Enthaelt n " +
                      "    ON a.Artikelnr = n.Artikelnr " +
                      "INNER JOIN Bestellungen b " +
                      "    ON n.Bestellnr = b.Bestellnr " +
                      "INNER JOIN Kunden k " +
                      "    ON b.Kundennr = k.Kundennr " +
                      "WHERE k.Name = 'Vogel' " +
                      "ORDER BY a.Bezeichnung;";
 
    rdr = cmd.ExecuteReader();
 
    while (rdr.Read()) {                  
        int artikelnr = rdr.GetInt32(0);                  
        string bezeichnung = rdr.GetString(1);
        decimal preis = rdr.GetDecimal(2);
        int herstellernr = rdr.GetInt32(3);
 
        textBox1.Text += bezeichnung + "\r\n";
    }
 
} catch (SqlCeException ex) {
    MessageBox.Show(ex.Message);
} catch (Exception ex) {
    MessageBox.Show(ex.Message);
} finally {
    rdr.Close();
    conn.Close();
}

Das gezeigte Beispiel ist ein exaktes Gegenstück zu der zuvor gezeigten LINQ-Anweisung.

DataReader

Der DataReader stellt ein Verfahren zum streng vorwärts gerichteten Lesen von Datensätzen aus einer Datenbank bereit. Der DataReader stellt eine ganze Fülle von Methoden zur Verfügung, um Spaltenwerte auszulesen, nämlich für jeden erdenklichen .NET-Datentyp eine. Für die SqlCeDataReader-Klasse sind es noch mehr, hier kommen die Get-Methoden für alle SQL Server Compact-Datentypen hinzu.

Die Methoden sind alle nach ihrem speziellen Datentyp, den sie zurückgeben, benannt, z.B. GetBoolean, GetDateTime, GetString etc. Wenn Sie diese Methoden verwenden, müssen sie ganz genau wissen, welchen Datentyp Sie erhalten. Dies wird auch dadurch erschwert, dass Methoden die Identifizierung der Spalte nur über deren Index (Position) zulassen. Der DataReader stellt summa summarum sehr primitive Methoden bereit, um mit den Datensätzen zu arbeiten.

DataAdapter

Der DataAdapter ist die wichtigste Klasse der .NET Data Provider. Mit ihm können Sie Ihren lokalen Datenspeicher füllen und Änderungen in die Datenbank zurückspeichern. Sie brauchen im Wesentlichen nur eine einzige Methode, um die Daten aus der Datenbank zu laden, und eine weitere Methode, um die Änderungen an den Daten wieder zurückzuspeichern. Der DataAdapter nutzt dabei intern weitere Klassen, die Sie entweder über den Designer oder über eigenen Programmcode erzeugen können.

Der DataAdapter arbeitet direkt mit dem lokalen Datenspeicher zusammen. Er füllt immer eine oder mehrere Tabellen ( DataTable-Objekte), entsprechend der Anzahl der Ergebnismengen des SelectCommands. Der Adapter fungiert als Brücke zwischen lokalen Datenspeicher und der Datenbank. Damit der DataAdapter Daten aus der Datenbank in den lokalen Datenspeicher laden kann, müssen einige Kriterien erfüllt sein. So muss ein DataTable-Objekt oder ein DataSet existieren. Weiterhin benötigen Sie ein Connection-Objekt und ausformulierte SQL-Abfragen. Zum Schluss muss der DataAdapter selbst erzeugt werden.

Sie haben bereits intensive Bekanntschaft mit der DataAdapter-Klasse gemacht, als Sie das DataSet generierten. In der vom Designer erzeugten Datei shopDataSet.Designer.cs finden Sie den DataAdapter für die SQL Server Compact-Datenbank, er nennt sich SqlCeDataAdapter. Der Designer hat nicht nur für jede Tabelle in der Datenbank einen Adapter erstellt und die Klassen SqlCeConnection, SqlCeCommand und SqlCeTransaction erzeugt, er hat auch alle Insert-, Update- und DeleteCommand-Objekte generiert. Gleichzeitig wurde für jede Tabelle auch das entsprechende DataTable-Objekt erstellt.

/// <summary>
///Represents the connection and commands used to retrieve and save data.
///</summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "2.0.0.0")]
[global::System.ComponentModel.DesignerCategoryAttribute("code")]
[global::System.ComponentModel.ToolboxItem(true)]
[global::System.ComponentModel.DataObjectAttribute(true)]
[global::System.ComponentModel.DesignerAttribute("Microsoft.VSDesigner.DataSource.Design.TableAdapterDesigner, Microsoft.VSDesigner" +
    ", Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[global::System.ComponentModel.Design.HelpKeywordAttribute("vs.data.TableAdapter")]
public partial class KundenTableAdapter : global::System.ComponentModel.Component {
 
    private global::System.Data.SqlServerCe.SqlCeDataAdapter _adapter;
 
    private global::System.Data.SqlServerCe.SqlCeConnection _connection;
 
    private global::System.Data.SqlServerCe.SqlCeTransaction _transaction;
 
    private global::System.Data.SqlServerCe.SqlCeCommand[] _commandCollection;
 
    private bool _clearBeforeFill;
 
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    public KundenTableAdapter() {
        this.ClearBeforeFill = true;
    }   
 
    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
    private void InitAdapter() {
        this._adapter = new global::System.Data.SqlServerCe.SqlCeDataAdapter();
        global::System.Data.Common.DataTableMapping tableMapping = new global::System.Data.Common.DataTableMapping();
        tableMapping.SourceTable = "Table";
        tableMapping.DataSetTable = "Kunden";
        tableMapping.ColumnMappings.Add("Kundennr", "Kundennr");
        tableMapping.ColumnMappings.Add("Name", "Name");
        tableMapping.ColumnMappings.Add("Vorname", "Vorname");
        tableMapping.ColumnMappings.Add("PLZ", "PLZ");
        tableMapping.ColumnMappings.Add("Ort", "Ort");
        tableMapping.ColumnMappings.Add("Strasse", "Strasse");
        tableMapping.ColumnMappings.Add("Hausnr", "Hausnr");
        this._adapter.TableMappings.Add(tableMapping);
        this._adapter.DeleteCommand = new global::System.Data.SqlServerCe.SqlCeCommand();
        this._adapter.DeleteCommand.Connection = this.Connection;
        this._adapter.DeleteCommand.CommandText = "DELETE FROM [Kunden] WHERE (([Kundennr] = @p1))";
        this._adapter.DeleteCommand.CommandType = global::System.Data.CommandType.Text;
        this._adapter.DeleteCommand.Parameters.Add(new global::System.Data.SqlServerCe.SqlCeParameter("@p1", global::System.Data.SqlDbType.Int, 0, global::System.Data.ParameterDirection.Input, true, 0, 0, "Kundennr", global::System.Data.DataRowVersion.Original, null));
        this._adapter.InsertCommand = new global::System.Data.SqlServerCe.SqlCeCommand();
        this._adapter.InsertCommand.Connection = this.Connection;
        this._adapter.InsertCommand.CommandText = "INSERT INTO [Kunden] ([Kundennr], [Name], [Vorname], [PLZ], [Ort], [Strasse], [Ha" +
            "usnr]) VALUES (@p1, @p2, @p3, @p4, @p5, @p6, @p7)";
        // ...

Der DataAdapter wird nicht als allein stehendes Objekt erzeugt, sondern zusammen mit den Command-Objekten in einem speziellen TableAdapter-Objekt zusammengefasst. Der TableAdapter ist eine neue Klasse, die allerdings nur in einer typisierten Form innerhalb des typisierten DataSets existiert. Der TableAdapter leitet sich direkt von System.ComponentModel.Component ab und enthält alle wesentlichen Objekte. Vor der Verwendung des TableAdapters muss dieser initialisiert werden. Das geschieht mit der Fill-Methode.

theTableAdapter.Fill(myDataSet.myTable);

Durch die vom Designer automatisch erstellten TableAdapter-Objekte können Sie in Ihrer Anwendung bequem mit dem DataSet arbeiten, die Kommunikation mit der Datenbank findet im Hintergrund über die Adapter-Klassen statt.


Benutzeroberfläche

Die Entwicklung kleiner Anwendungen, die mit einer Datenbank interagieren gestaltet sich in .NET mit den in diesem Artikel gezeigten Techniken außerordentlich einfach. Die eigentliche Aufgabe liegt neben dem Datenbankentwurf, in der Gestaltung der grafischen Benutzeroberflächen.

In der heutigen Zeit gibt es kaum noch Arbeitsplätze, die ohne Computer auskommen. Viele Aufgaben werden durch den Rechner automatisiert und helfen dabei effizienter zu arbeiten. Dennoch zeigt sich immer wieder, wie schwer es manchen fällt, ein neu eingeführtes Anwendungssystem zu bedienen. Die Gestaltung einer grafischen Benutzeroberfläche ist meist eine unterschätzte und oft schwierige Aufgabe. Unser kleines Geschäft soll unter anderen eine Oberfläche besitzen, mit dem der Nutzer neue Kunden in die Liste aufnehmen kann. Sie haben bereits im Abschnitt über DataSets erfahren, wie Sie eine Datenbindung direkt vom Designer vornehmen lassen können, um diese entweder in einem DataGridView oder einem Eingabefeld bearbeiten zu können.

Der Designer hilft Ihnen aber spätestens dann nicht mehr weiter, wenn Sie auf komplexere Art und Weise Daten auslesen und manipulieren wollen. An dieser Stelle bietet sich dann LINQ to SQL förmlich an. In LINQ to SQL wird das Datenmodell einer relationalen Datenbank einem Objektmodell zugeordnet, das in der Programmiersprache des Entwicklers ausgedrückt ist. Wenn Sie die Anwendung ausführen, übersetzt LINQ to SQL die sprachintegrierten Abfragen im Objektmodell in SQL und sendet sie zur Ausführung an die Datenbank. Wenn die Datenbank die Ergebnisse zurückgibt, übersetzt LINQ to SQL diese zurück in Objekte, die Sie mit Ihrer eigenen Programmiersprache bearbeiten können.

Auf der Benutzeroberfläche für neue Kunden befinden sich die üblichen Standardfelder, mit denen die Daten von Neukunden erfasst werden können. Das Formular wird über einen Button abgeschickt. In dem EventHandler des Buttons, wird der LINQ-Code untergebracht, nachdem einige kleine Integritätsprüfungen vorgenommen wurden.

private void buttonOk_Click(object sender, EventArgs e)
{
    // Add some checks here
    if (maskedTextBoxName.Text.Length < 3 || maskedTextBoxSurname.Text.Length < 3 ||
        maskedTextBoxCity.Text.Length < 3 || maskedTextBoxStreet.Text.Length < 3 ||
        maskedTextBoxPostal.Text.Length < 5 || maskedTextBoxNumber.Text.Length < 1) {
        return;
    }
 
    int tPlz = 0;
    short tHausnr = 0;
 
    try {
        tPlz = Convert.ToInt32(maskedTextBoxPostal.Text);
        tHausnr = Convert.ToInt16(maskedTextBoxNumber.Text);
    } catch (Exception ex) {
        MessageBox.Show(ex.Message);
        return; // Return from this function
    }
 
    string connString = global::Shop.Properties.Settings.Default.shopConnectionString;
 
    ShopDatabase db = new ShopDatabase(connString);
 
    // Create a new Kunden object
    Kunden kunden = new Kunden {
        Name = maskedTextBoxName.Text,
        Vorname = maskedTextBoxSurname.Text,
        PLZ = tPlz,
        Ort = maskedTextBoxCity.Text,
        Strasse = maskedTextBoxStreet.Text,
        Hausnr = tHausnr
    };
 
    db.Kunden.InsertOnSubmit(kunden);
 
    // Submit the change to the database
    try {
        db.SubmitChanges();
        // Close on success
        MessageBox.Show("Ein neuer Kunde wurde erfolgreich hinzugefügt.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
        this.Close();
    } catch (Exception ex) {
        MessageBox.Show(ex.Message);
    }
}

An dem Quellcode können Sie bereits erkennen, dass sich der Aufwand dank LINQ sehr in Grenzen hält. Noch mehr Ersparnis erzielt man allerdings bei umfangreicheren Benutzeroberflächen und Aufgaben. Wir haben uns auf CodePlanet entschieden am Ende dieses Artikels keine weiteren Bilder der fertigen Anwendung, mit all seinen Formularen und Eigenschaften, zu präsentieren. Stattdessen können Sie sich in dem folgenden kurzen Flash-Video die fertige Anwendung in Ruhe ansehen.

Schluss

In diesem Artikel haben Sie die vielschichtigen Möglichkeiten der SQL Server Compact-Datenbank von Microsoft kennengelernt. Mit ADO.NET und LINQ to SQL wurden dem Programmierer leistungsstarke Werkzeuge zur Verfügung gestellt, um die eigene Produktivität weiter zu erhöhen. Es liegt an Ihnen, diese Werkzeuge in Zukunft auch zu nutzen. Zum Abschluss finden Sie die Visual Studio Projektmappe in der Download-Rubrik.

Zuletzt aktualisiert am Freitag, den 14. Januar 2011 um 09:13 Uhr
 
AUSWAHLMENÜ