Zugriff auf Datenbanken unter .NET mit dem Entity

Werbung
Hausarbeit
FOM – Fachhochschule für
Oekonomie & Management
Frankfurt am Main
Datenbankmanagement
3. Semester
Hausarbeit zum Thema
Zugriff auf Datenbanken unter .NET mit dem
Entity Framework
Betreuer: Professor Dr. rer. pol. Dieter Litzinger, Erik Reischl
Autor:
Max Jäger
Frankfurt, den 06. Januar 2013
Inhalt
Inhalt
Abkürzungen
II
Abbildungen
III
Quelltexte
IV
1 Einleitung
1
2 Überblick
1
2.1
Objektrelationale Mapper . . . . . . . . . . . . . . . . . . . . .
1
2.2
Architektur des ADO.NET Entity Framework . . . . . . . . . .
2
3 Das Entitätenmodell
3
3.1
Entitäten und deren Beziehungen . . . . . . . . . . . . . . . . .
3
3.2
Objektkontext . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
3.3
Metadaten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
3.3.1
Logische Schicht . . . . . . . . . . . . . . . . . . . . . . .
5
3.3.2
Konzeptionelle Schicht . . . . . . . . . . . . . . . . . . .
5
3.3.3
Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
Entwurf des Entitätenmodells . . . . . . . . . . . . . . . . . . .
6
3.4
4 Arbeiten mit Entitätsdaten
7
4.1
Abfragen eines konzeptionellen Modells mit LINQ to Entities . .
7
4.2
Hinzufügen, Ändern und Löschen von Objekten . . . . . . . . .
8
4.2.1
Hinzufügen von Objekten . . . . . . . . . . . . . . . . .
8
4.2.2
Ändern von Objekten . . . . . . . . . . . . . . . . . . . .
9
4.2.3
Löschen von Objekten . . . . . . . . . . . . . . . . . . .
9
5 Fazit
10
6 Literatur
11
I
Abkürzungen
Abkürzungen
CSDL . . . . . . . . . . . Conceptual Schema Definition Language
EDM . . . . . . . . . . . . Entity Data Model
LINQ . . . . . . . . . . . Language Integrated Query
MSL . . . . . . . . . . . . . Mapping Schema Language
O/R-Mapper . . . Objektrelationaler Mapper
SSDL . . . . . . . . . . . . Storage Schema Definition Language
II
Abbildungen
Abbildungen
1
Entity Framework-Architektur . . . . . . . . . . . . . . . . . . .
III
2
Quelltexte
Quelltexte
1
SSDL-Abschnitt des EDM . . . . . . . . . . . . . . . . . . . . .
5
2
CSDL-Abschnitt des EDM . . . . . . . . . . . . . . . . . . . . .
5
3
MSL-Abschnitt des EDM . . . . . . . . . . . . . . . . . . . . . .
6
4
Abfragen von Objekten . . . . . . . . . . . . . . . . . . . . . . .
7
5
Hinzufügen eines Objektes . . . . . . . . . . . . . . . . . . . . .
8
6
Ändern eines Objektes . . . . . . . . . . . . . . . . . . . . . . .
9
7
Löschen eines Objektes . . . . . . . . . . . . . . . . . . . . . . .
9
IV
Überblick
1
Einleitung
Bei vielen Anwendungen muss eine große Menge an Informationen gespeichert werden. Für die Speicherung dieser Daten werden häufig relationale
Datenbanken verwendet. Bei der Programmierung mit objektorientierten Programmiersprachen stehen Entwickler vor der Aufgabe, sowohl Klassenmodelle
zu entwerfen und Objekte und deren Beziehungen zu verwalten, als auch die
Daten in der Datenbank zu speichern.1 Der Zugriff auf Datenbanken lässt
sich aus Sicht der Programmierung auf mehrere Arten realisieren, von denen eine die Verwendung eines objektrelationalen Mappers (O/R-Mapper) ist.
Diese Hausarbeit bietet nach einer kurzen Erläuterung der Funktionsweise
eines O/R-Mappers eine Einführung in das ADO.NET Entity Framework, dem
O/R-Mapper für die Programmiersprache C#.
2
Überblick
2.1
Objektrelationale Mapper
Ein O/R-Mapper ist eine Zwischenschicht zwischen einer relationalen Datenbank und einer Anwendung. In einer relationalen Datenbank gibt es Tabellen,
die über Relationen miteinander verknüpft werden können, und auf Anwendungsseite gibt es in objektorientierten Programmiersprachen Klassen und
Objekte. Der O/R-Mapper übernimmt die Verbindung (Mapping) zwischen den
Tabellen der Datenbank und den Klassen der Anwendung. Ein O/R-Mapper
erzeugt aus Objekten SQL-Abfragen bzw. aus Ergebnissen von SQL-Abfragen
wieder Objekte. Hierbei werden einzelne Datensätze in der Datenbanktabelle
in einzelne Objekte umgesetzt, die Spalten der Datenbanktabelle werden in
die entsprechenden Eigenschaften der Objekte umgesetzt.2 Für das Verständnis sind Kenntnisse in Bezug auf die Datenbank-Struktur und SQL-Abfragen
hilfreich, z. B. im Hinblick auf Geschwindigkeitsoptimierungen von Abfragen.3
1
2
3
Vgl. Microsoft, (2012a), o.S.
Vgl. Kansy, (2011), S. 20.
Vgl. Mostarda, Sanctis und Bochicchio, (2011), S. 25.
1
Überblick
2.2
Architektur des ADO.NET Entity Framework
Das ADO.NET Entity Framework ist in mehrere Ebenen unterteilt. Zentrale
Stelle ist der EntityClient Data Provider. Dieser stellt die Verbindung zwischen
den Klassen und Objekten in .NET und der Datenbank her.4 Hinterlegt sind
die dafür notwendigen Informationen im Entitätenmodell (EDM). Dieses ist in
drei Schichten aufgeteilt:5
• die logische Schicht, die die Datenbankstruktur darstellt (Storage Model)
• die konzeptionelle Schicht, die das Datenmodell darstellt (Conceptual
Model)
• die Zuordnungsschicht, die die logische und die konzeptionelle Schicht
miteinander verbindet (Mapping)
Quelle: Entnommen aus: http://msdn.microsoft.com/de-de/library/vstudio/bb399567, Zugriffsdatum: 2012-10-03
Abbildung 1:
4
5
Entity Framework-Architektur
Vgl. Lerman, (2010), S. 14.
Vgl. Mertins, Neumann und Kühnel, (2012), S. 1151.
2
Das Entitätenmodell
Mit den Informationen des EDM ist der EntityClient Data Provider in der Lage,
datenbankspezifische ADO.NET Data Provider anzusprechen und Zugriffe auf
unterschiedlichen Datenbanksystemen auszuführen.6
Innerhalb der .NET-Programmiersprachen bietet das Entity Framework dem
Entwickler zwei Möglichkeiten, um Daten abzufragen: LINQ to Entities und
Entity SQL. LINQ ist eine SQL-ähnliche Abfragesprache direkt auf Objektebene.
Entity SQL ist eine datenbankunabhängige SQL-Syntax. LINQ to Entities
hat sich mittlerweile als Standard-Abfragesprache etabliert, jedoch gibt es
Anwendungsfälle, in denen LINQ to Entities nicht möglich ist. Die Abfragen
bei LINQ to Entities müssen zur Kompilierzeit feststehen, wohingegen sich
eine Entity SQL-Abfrage dynamisch zur Laufzeit erzeugen und ausführen
lässt.7 Bei beiden Verfahren übersetzt der EntityClient Data Provider die
erzeugten Abfragen in die entsprechenden SQL-Abfragen und liefert einen
Datenleser zurück. Die Object Services übersetzen die gelesenen Daten in die
entsprechenden Objekte.8
3
Das Entitätenmodell
Nachdem die Architektur des Entity Frameworks erläutert wurde, werden nun
die einzelnen Bestandteile des Entitätenmodells erläutert. Hierzu zählen die
Klassen, mit denen der Entwickler programmiert, der Objektkontext, der die
Verbindung zur Datenbank herstellt, und die Metadaten, mit denen die Klassenund die Datenbankstrukturen abgebildet und verknüpft werden.9
3.1
Entitäten und deren Beziehungen
Entitäten sind Klassen, die Datenbanktabellen repräsentieren, deren Tabellenspalten als Eigenschaften der Klassen abgebildet werden. Neben skalaren
Werten können Klassen über Navigationseigenschaften verfügen, die die Beziehungen, die in der Datenbankbank als Relationen hinterlegt sind, abbilden.
6
7
8
9
Vgl.
Vgl.
Vgl.
Vgl.
Lerman, (2010), S. 49f.
Mertins, Neumann und Kühnel, (2012), S. 1200.
Microsoft, (2012a), o.S.
Microsoft, (2012a), o.S.
3
Das Entitätenmodell
Hierüber kann von einer zu einer anderen Klasse navigiert werden, die mit dieser
über eine Zuordnung verbunden ist. Zudem verfügt jede Klasse über einen
Primärschlüssel, der eine Ausprägung dieser Klasse eindeutig identifizert.10
3.2
Objektkontext
Der Objektkontext verwaltet die Verbindung zwischen den Entitäten und der
Datenbank. Er überwacht die Änderungen an den Objekten, die Einhaltung
der definierten Beziehungen der Objekte untereinander und stellt über eine
SaveChanges-Methode die zentrale Funktion bereit, die Einfügungen, Änderungen und Löschungen in die Datenquelle schreibt.11 Hierzu werden datenbankspezifische Befehle generiert und ausgeführt, die die entsprechenden Änderungen
in der Datenbank vornehmen.12
3.3
Metadaten
Wie bereits in der Architektur dargestellt, besteht das Entitätenmodell intern
aus drei Schichten. Diese drei Schichten werden in einer gemeinsamen Datei mit
der Dateiendung .edmx gespeichert, die einen XML-Aufbau besitzt.13 Innerhalb
dieser Datei gibt es für jede einzelne Schicht einen eigenen Abschnitt.14
Durch diese Trennung ist es möglich, das gleiche Klassenmodell mit unterschiedlichen Datenprovidern zu verwenden, indem lediglich das Datenbankmodell
und die Zuordnungen ausgetauscht werden. Über diese drei Schichten kann
das Entity Framework aus den Vorgängen, die auf die Entitäten angewendet
werden, die entsprechenden Vorgänge für die Datenbank ableiten.15
10
11
12
13
14
15
Vgl.
Vgl.
Vgl.
Vgl.
Vgl.
Vgl.
Kansy, (2011), S. 81.
Microsoft, (2012a), o.S.
Microsoft, (2012b), o.S.
Kansy, (2011), S. 27.
Mertins, Neumann und Kühnel, (2012), S. 1162.
Microsoft, (2012a), o.S.
4
Das Entitätenmodell
3.3.1
Logische Schicht
Die logische Schicht beschreibt das physische Datenbankmodell. Darunter fallen
die Datenbanktabellen mit ihren Spalten, Datentypen, Primärschlüsseln und
Beziehungen untereinander. Die Beschreibung erfolgt mit der Storage Schema
Definition Language (SSDL).16
Für die Verdeutlichung der Zuordnung wird die Datenbanktabelle mit englischen
Bezeichnungen angegeben und die .NET-Klasse mit deutschen Bezeichnern. Eine
Tabelle wird über ein EntityType-Element definiert. Der Primärschlüssel wird
im Unterelement Key, die einzelnen Spalten in Property-Elementen definiert.
Jedes Property-Element hat mehrere Attribute, die z. B. den Namen, den
Datentyp oder die maximale Länge festlegen. Der Datentyp bezieht sich hierbei
auf den Datentyp des Datenbanksystems.17
Quelltext 1:
1
2
3
4
5
6
7
8
9
SSDL-Abschnitt des EDM
< EntityType Name = " Address " >
<Key >
< PropertyRef Name = " AddressID " / >
</ Key >
< Property Name = " AddressID " Type = " int " Nullable = " false " / >
< Property Name = " Street " Type = " nvarchar " MaxLength = " 100 " / >
< Property Name = " Postcode " Type = " nvarchar " MaxLength = " 5 " / >
< Property Name = " City " Type = " nvarchar " MaxLength = " 100 " / >
</ EntityType >
3.3.2
Konzeptionelle Schicht
Die Beschreibung der .NET-Klassen erfolgt in der konzeptionellen Schicht
mit der Conceptual Schema Definition Language (CSDL). Dieser Abschnitt
hat große Ähnlichkeit mit der Definition der logischen Schicht, jedoch gibt
es erwähnenswerte Unterschiede. Beispielsweise beziehen sich die Datentypen
hier auf die .NET-Datentypen und nicht mehr auf die datenbankspezifischen
Datentypen.18
16
17
18
Vgl. Mertins, Neumann und Kühnel, (2012), S. 1163.
Vgl. Mertins, Neumann und Kühnel, (2012), S. 1163.
Vgl. Doberenz und Gewinnus, (2010), S. 1174.
5
Das Entitätenmodell
Quelltext 2:
1
2
3
4
5
6
7
8
9
CSDL-Abschnitt des EDM
< EntityType Name = " Adresse " >
<Key >
< PropertyRef Name = " AdressNr " / >
</ Key >
< Property Name = " AdressNr " Type = " Int32 " Nullable = " false " / >
< Property Name = " Strasse " Type = " String " MaxLength = " 100 " / >
< Property Name = " PLZ " Type = " String " MaxLength = " 5 " / >
< Property Name = " Ort " Type = " String " MaxLength = " 100 " / >
</ EntityType >
3.3.3
Mapping
Die Verbindung zwischen der logischen und der konzeptionellen Schicht wird
mit der Mapping Schema Language (MSL) beschrieben. Die Tabellen werden
auf die .NET-Klassen und die einzelnen Spalten auf die Eigenschaften der
Klassen abgebildet.19
Quelltext 3:
1
2
3
4
5
6
7
8
9
10
MSL-Abschnitt des EDM
< E n t i t y S e t M a p p i n g Name = " Adresse " >
< E n t i t y T y p e M a p p i n g TypeName = " Adresse " >
< M ap pi ng F ra gm en t StoreEntity Set = " Address " >
< ScalarPr operty Name = " AdressNr " ColumnName = " AddressID " / >
< ScalarPr operty Name = " Strasse " ColumnName = " Street " / >
< ScalarPr operty Name = " PLZ " ColumnName = " Postcode " / >
< ScalarPr operty Name = " Ort " ColumnName = " City " / >
</ MappingFragment >
</ EntityTypeMapping >
</ EntitySetMapping >
3.4
Entwurf des Entitätenmodells
Dem Entwickler stehen zwei Ansätze zur Erzeugung des Entitätenmodells zur
Verfügung: Database-First oder Model-First. Beim Database-First-Ansatz wird
zunächst die Datenbank mit allen Tabellen, Spalten, Primär- und Fremdschlüsseln erzeugt. Danach kann das Entitätenmodell aus dieser Datenbank generiert
werden. Der Model-First-Ansatz geht den umgekehrten Weg, bei dem zunächst
das Klassenmodell erstellt und daraus die Datenbank erzeugt wird.20
19
20
Vgl. Mertins, Neumann und Kühnel, (2012), S. 1165.
Vgl. Doberenz und Gewinnus, (2010), S. 1178f.
6
Arbeiten mit Entitätsdaten
4
Arbeiten mit Entitätsdaten
Als meistgenutzte Abfragesprache des Entity Frameworks hat sich LINQ to
Entities etabliert. Dieses Kapitel bietet einen Überblick über die Möglichkeiten,
mit dieser Abfragesprache sowohl Daten aus der Datenbank auszulesen als auch
dort anzulegen, zu ändern oder zu löschen. Um Daten aus der Datenbank auszulesen oder dort zu speichern, muss zunächst eine Instanz des Objektkontextes
erstellt werden. Darüber ist der Zugriff auf alle Entitäten des Entitätenmodells
möglich.21
4.1
Abfragen eines konzeptionellen Modells mit LINQ
to Entities
Als Beispiel für eine Abfrage von Objekten dient die Klasse Adresse, die bereits
bei der Beschreibung der Schichten des Entitätenmodells verwendet wurde. Es
wird zunächst ein Objekt der Klasse DbContext erzeugt, das den Objektkontext
repräsentiert. Danach wird aus dem soeben erzeugten Kontext eine Liste von
Adressen ausgelesen, bei denen Testingen als Ort hinterlegt ist. Hierdurch
findet also gleichzeitig eine Filterung statt. Diese Liste wird der Variablen
adressen zugewiesen. Danach werden alle Adressen auf der Kommandozeile
ausgegeben.
Quelltext 4:
Abfragen von Objekten
1
2
3
4
5
6
7
8
// D a t e n b a n k k o n t e x t erstellen
using ( DbContext context = new DbContext () )
{
// alle Adressen aus " Testingen " auslesen
var adressen = context . Adresse . Where ( a = > a . Ort == " Testingen " ) ;
// gefundene Adressen auf der Kommandozeile ausgeben
adressen . ForEach ( a = > Console . WriteLine ( a ) ) ;
}
21
Vgl. Mostarda, Sanctis und Bochicchio, (2011), S. 55f.
7
Arbeiten mit Entitätsdaten
4.2
Hinzufügen, Ändern und Löschen von Objekten
Das Entity Framework protokolliert alle Änderungen an den Objekten. Wird ein
neues Objekt angelegt, so wird der Status auf added gesetzt. Werden Objekte
aus der Datenbank gelesen, bekommen diese den Status unchanged. Wird eine
Eigenschaft gesetzt, so wird der Status auf modified gesetzt. Durch diesen
Mechanismus weiß das Entity Framework, welche Art von Befehl es für die
Datenbank erzeugen muss. Innerhalb der SaveChanges-Methode wird der Status
des Objektes wieder auf unchanged gesetzt.22
4.2.1
Hinzufügen von Objekten
Um der Datenquelle Daten hinzuzufügen, wird eine neue Instanz der entsprechenden Entitätsklasse erstellt, mit den entsprechenden Werten befüllt
und dem Objektkontext hinzugefügt. Es muss darauf geachtet werden, dass
alle Eigenschaften, die keine Null-Werte zulassen, auch gesetzt sind. Nachdem die Instanz dem Objektkontext hinzugefügt wurde, kann auf diesem die
SaveChanges-Methode aufgerufen werden, die die Daten der erzeugten Instanz
in der Datenbank abspeichert.23
Quelltext 5:
1
2
3
4
5
6
7
8
9
10
11
12
13
22
23
Hinzufügen eines Objektes
// D a t e n b a n k k o n t e x t erstellen
using ( DbContext context = new DbContext () )
{
// Adress - Objekt erstellen und Eigenschaften festlegen
Adresse adresse = new Adresse () ;
adresse . Strasse = " Musterstraße 1 " ;
adresse . PLZ = " 12345 " ;
adresse . Ort = " Testingen " ;
// die Adresse dem Objektkontext hinzufügen
context . AddObject ( adresse ) ;
// Speichern
context . SaveChanges () ;
}
Vgl. Microsoft, (2012b), o.S.
Vgl. Microsoft, (2012b), o.S.
8
Arbeiten mit Entitätsdaten
4.2.2
Ändern von Objekten
Um einen Datensatz zu ändern, muss zunächst ein Datensatz aus der Datenquelle ausgelesen werden. Nachdem dies geschehen ist, können die gewünschten
Änderungen durchgeführt werden, indem die Eigenschaften gesetzt werden. Zu
beachten ist, dass beim Setzen von Eigenschaften der Status des Objektes auch
auf modified gesetzt wird, wenn der gleiche Eigenschaftswert gesetzt wird. Durch
den Aufruf der SaveChanges-Methode wird der Datensatz in der Datenbank
geändert.24
Quelltext 6:
1
2
3
4
5
6
7
8
9
10
11
12
Ändern eines Objektes
// D a t e n b a n k k o n t e x t erstellen
using ( DbContext context = new DbContext () )
{
// die erste Adresse aus " Testingen " auslesen
var adresse = context . Adresse
. Where ( a = > a . Ort == " Testingen " )
. First () ;
// die Straße der gefundenen Adresse ändern
adresse . Strasse = " Am Test 1 " ;
// Speichern
context . SaveChanges () ;
}
4.2.3
Löschen von Objekten
Für das Löschen von Objekten stellt der Objektkontext die Methode DeleteObject zur Verfügung. Beim Aufruf dieser Methode mit einem Objekt wird dieses
in der Datenbank als zu löschen gekennzeichnet. Erst durch den Aufruf der
SaveChanges-Methode wird der Datensatz auch wirklich aus der Datenbank
gelöscht.25
Quelltext 7:
Löschen eines Objektes
1
2
3
4
// D a t e n b a n k k o n t e x t erstellen
using ( DbContext context = new DbContext () )
{
// die erste Adresse aus " Testingen " auslesen
24
Vgl. Microsoft, (2012b), o.S.
Vgl. Microsoft, (2012b), o.S.
25
9
Fazit
5
6
7
8
9
10
11
12
5
var adresse = context . Adresse
. Where ( a = > a . Ort == " Testingen " )
. First () ;
// die gefundene Adresse löschen
context . DeleteObject ( adresse ) ;
// Speichern
context . SaveChanges () ;
}
Fazit
Das Entity Framework bietet eine gute Möglichkeit, auf eine Datenbank zuzugreifen, und dabei in dem für den Programmierer gewohnten Kontext von
.NET zu bleiben und mit Klassen und Objekten zu arbeiten. Dadurch kann
eine schnelle Entwicklung erfolgen, und durch die Unterstützung einer Entwicklungsumgebung, wie z. B. Visual Studio, lassen sich Fehler vermeiden. Der
Entwickler muss sich nicht mehr mit dem Verwaltungsaufwand zum Herstellen
einer Verbindung zur Datenbank, zum Auslesen der Daten und Übertragen in
die Entitäten beschäftigen.26
Es gibt zudem diverse Datenbanksysteme, mit denen sich der Entwickler auskennen müsste. Das Entity Framework stellt die Erzeugung korrekter SQL-Abfragen
unter Berücksichtigung der datenbankspezifischen Besonderheiten sicher. Dadurch nimmt es ihm die Arbeit ab, datenbankspezifische Abfragen erzeugen zu
müssen.27
Jedoch sollte immer der spezifische Anwendungsfall betrachtet werden, ob
der Einsatz des Entity Frameworks sinnvoll ist. Bei Massenaktualisieren zum
Beispiel ist das Entity Framework nicht das beste Verfahren, da hierbei der
gesamte Verwaltungsmechanismus viel Performance kostet.28
26
27
28
Vgl. Lerman, (2010), S. 1f.
Vgl. Mostarda, Sanctis und Bochicchio, (2011), S. 25.
Vgl. Mostarda, Sanctis und Bochicchio, (2011), S. 26.
10
Literatur
6
Literatur
1. Doberenz, Walter und Thomas Gewinnus (2010): DatenbankProgrammierung mit Visual C# 2010: Grundlagen, Rezepte, Anwendungsbeispiele. Microsoft Press Deutschland. isbn: 978-3866454460.
2. Kansy, Thorsten (2011): Programmieren mit dem ADO.NET Entity Framework. Microsoft Press Deutschland. isbn: 978-3866454613.
3. Lerman, Julia (2010): Programming Entity Framework. O’Reilly Media.
isbn: 978-0596807269.
4. Mertins, Dirk, Jörg Neumann und Andreas Kühnel (2012): SQL Server
2012: Das Programmierhandbuch. Inkl. ADO.NET 4.0 Entity Framework.
Galileo Computing. isbn: 978-3836219440.
5. Microsoft (2012a): Entity Framework-Architektur. url: http://msdn.
microsoft . com / de - de / library / vstudio / bb399567 (besucht am
03. 10. 2012).
6. — (2012b): Hinzufügen, Ändern und Löschen von Objekten (Entity
Framework). url: http : / / msdn . microsoft . com / de - de / library /
bb738695 (besucht am 10. 10. 2012).
7. Mostarda, Stefano, Marco De Sanctis und Daniele Bochicchio (2011):
Microsoft Entity Framework in Action. Manning. isbn: 978-1935182184.
11
Ehrenwörtliche Erklärung
Hiermit versichere ich, dass die vorliegende Hausarbeit von
mir selbstständig und ohne unerlaubte Hilfe angefertigt worden ist, insbesondere, dass ich alle Stellen, die wörtlich oder
annähernd wörtlich aus Veröffentlichungen entnommen sind,
durch Zitate als solche gekennzeichnet habe. Ich versichere
auch, dass die von mir eingereichte schriftliche Version mit der
digitalen Version übereinstimmt. Weiterhin erkläre ich, dass
die Hausarbeit in gleicher oder ähnlicher Form noch keiner
anderen Prüfungsbehörde vorgelegen hat.
Frankfurt am Main, den
6. Januar 2013
Max Jäger
Herunterladen