Isolated Storage unter Microsofts Windows Phone 7

Seite 2: Anbindung an die Datenbank

Inhaltsverzeichnis

Wer komplexere oder gar strukturierte Daten verarbeiten will, wünscht sich schnell eine relationale Datenbank. Unter Windows Mobile gab es eine kleine Version von Microsofts Datenbankengine, die aber in Windows Phone 7.0 fehlt. Erst ab dem Mango-Release des Betriebssystems findet sich auch am Windows Phone eine kleine Datenbankengine. Leider ist sie bei weitem nicht so leistungsfähig wie die in Windows Mobile integrierte. Sie kann nur mit im Isolated Storage oder im Ressourcenverzeichnis gespeicherten Datenbanken umgehen und nimmt keinen Kontakt zu SQL-Servern oder sonstigen Datenquellen auf.

Mehr Infos

Normalisierung

Der Begriff Normalisierung steht für die redundanzreduzierende Aufteilung von Datensätzen in mehrere, miteinander verknüpfte Tabellen. In einem normalen Karteikasten würde man den Namen des Status einfach auf jede Kundenkarteikarte schreiben – ändert sich der Name eines Status, muss man alle Karten umschreiben.

Normalisiert hätte man zwei Karteikästen: Einer enthält die Kunden, ein weiterer die mit einem einzigartigen Primärschlüssel versehenen Status. In den Kundenkarteiblättern findet man ab nun nur mehr den Primärschlüssel des dem Kunden zugeordneten Status, was Platz spart und Änderungen erleichtert.

Im Kern verwendet das System für die Kommunikation eine LINQ to SQL genannte Engine. Die Grundidee dahinter ist, den Zugriff auf die in der Datenbank gespeicherten Daten anstatt über SQL-Strings direkt in der Programmiersprache abzuwickeln. Dadurch erspart man sich das Hantieren mit SQL, bindet sich aber auch "auf ewiglich" an die Datenbankengine von Microsoft. Eine primitive Datenbankapplikation ohne GUI zeigt das Beispiel WP7Storage7 (nur unter Mango lauffähig). Die Idee dahinter besteht darin, die dynamischen Zusammenhänge in der Datenbankanbindung zu visualisieren. Als Thema sei aus gegebenem Anlass die Beziehung zwischen Vielfliegern und ihren Statuskarten herangezogen. Die Fluggesellschaft Air Berlin hat erst vor Kurzem eine neue Karte eingeführt, hier sieht man gleich den Grund für das "Normalisieren" der Datensätze.

Entwickler, die in ihrem Programm auf LINQ to SQL zurückgreifen möchten, müssen zunächst eine Referenz auf System.Data.Linq zur Projektmappe hinzufügen. Danach erstellt man sich für jede Tabelle eine "Datenklasse", die die in der Tabelle zu speichernden Werte abbildet. Hier die Kundenklasse der Datei Kunden.cs:

[Table]
public class Kunden
{
[Column(IsPrimaryKey = true, IsDbGenerated = true,
DbType = "INT NOT NULL Identity", CanBeNull = false,
AutoSync = AutoSync.OnInsert)]
public int myId;

[Column]
public int myStatus;

[Column]
public String myName;

[Column]
public String mySurName;
}

Die Definition von Datenklassen beginnt in LINQ to SQL immer mit dem Attribut [Table]. Es weist den Compiler darauf hin, dass die Instanzen der folgenden Klasse gemeinsam eine Tabelle in der Datenbank belegen werden. Einzelne Datenspalten weist das Attribut [Column] aus. Während die Spalten mit der Meilenzahl, dem Namen und dem Vornamen keiner weiteren Eigenschaften bedürfen, legt IsPrimaryKey die Eigenschaft myId als Primärschlüssel der Tabelle fest. Durch das Setzen von IsDbGenerated weist man die Datenbank an, die ID zu generieren. Weitere Attribute bietet die Statusklasse:

[Table]
public class Statii
{
[Column(IsPrimaryKey = true, IsDbGenerated = true,
DbType = "INT NOT NULL Identity", CanBeNull = false,
AutoSync = AutoSync.OnInsert)]
public int myStatusId;

[Column]
public String myName;

private EntitySet<Kunden> _Kunden;
[Association(Storage = "_Kunden", OtherKey = "myStatus")]
public EntitySet<Kunden> myKunden
{
get { return this._Kunden; }
set { this._Kunden.Assign(value); }
}

public Statii()
{
_Kunden = new EntitySet<Kunden>();
}
}

Wie in der Kundenklassen wird hier zunächst ein Primärschlüssel als Attribut definiert. Für diese Klasse ist das auch wirklich notwendig, da die Kunden ja über diese ID auf den ihnen zugehörigen Status verweisen. Allein aus Bequemlichkeitsgründen soll jeder Status eine Liste aller zu ihm gehörenden Kunden aus der Datenbank enthalten. Dazu ist als Erstes ein EntitySet zu erstellen, das diese Daten aufnimmt. Die Beziehung zwischen dem EntitySet (Storage) und dem Fremdschlüssel in der Kundentabelle (OtherKey) ist im Association-Attribut dargestellt. Da die Klasse nur einen Primärschlüssel hat, wird der automatisch als "Quelle" der Relation angesehen.

Damit sind die Datenklassen fertig. Als Nächstes muss man einen DataContext erstellen, eine Adapterklasse, die die Kommunikation zwischen den Datenklassen und der Datenbank verantwortet. Sie enthält zusätzlich Informationen über das Schema der Datenbank:

public class MyDataContext : DataContext
{
public string myConnStr;
public MyDataContext(string connStr)
: base(connStr)
{
myConnStr = connStr;
}

public Table<Statii> Statii
{
get
{
return this.GetTable<Statii>();
}
}
public Table<Kunden> Kunden
{
get
{
return this.GetTable<Kunden>();
}
}
}

In LINQ hat der DataContext unter anderem die Aufgabe, für die Struktur der Datenbank zu sorgen. Aus diesem Grund hat die im Beispiel gezeigte Implementierung zwei Eigenschaften, die je eine Tabelle der Datenbank repräsentieren.

Zuletzt benötigt man noch die Datei, die die eigentliche Datenbank enthält und eine Instanz des DataContext im Konstruktor des Formulars:

// Constructor
public MainPage()
{
InitializeComponent();
myDb = new MyDataContext("Data Source=isostore:/DB.sdf");
using (var db = myDb)
{
if (!db.DatabaseExists())
db.CreateDatabase();
}

Beim Erstellen der DataContext-Instanz verwendet man einen String, der auf die vom DataContext zu verwendende Datenquelle zeigt. Unter Windows Phone 7 muss die Datenquelle immer eine Datei im Isolated Storage sein. Sie wird mit dem mit isostore:/ beginnenden URL angegeben.

Sobald der DataContext aktiv ist, muss man prüfen, ob die Datenbank existiert. Wenn nicht, erstellen man sie durch einen Aufruf von CreateDatabase. Wie bereits erwähnt, enthält der DataContext alle zum Erstellen der Datenbank erforderlichen Informationen.