Entity Framework
Das ADO.NET Entity Framework (kurz: ADO.NET EF) ist ein ORM-Framework von Microsoft, welches auf ADO.NET basiert.
Modellieransätze
Code First | Model First | |
---|---|---|
Keine Datenbank vorhanden | Bestehende Klassen werden mit Annotationen (Table , Column ) ausgezeichnet, welche die Abbildung auf eine Datenbank steuern. Darauf aufbauend werden vom DbContext die Datenbank und die Datenbank-Tabellen modelliert und beim Aufruf der SaveChanges() -Methode erstellt.
|
Die Entity-Klassen werden mit einem grafischen Designer modelliert. Das Modell wird einerseits mit Hilfe des Text Template Transformation Toolkit (T4) und der zugehörigen T4-Skriptsprache in Entity-Klassen umgewandelt. Zudem erlaubt es der Designer, ein SQL-Skript zu erstellen, mit dem die Datenbank erstellt wird. |
Verwendung einer bestehenden Datenbank | Die Entity-Klassen können entsprechend der vorgegebenen Datenbank manuell erstellt, modelliert und ausgezeichnet werden. Dies ist jedoch sehr arbeitsintensiv. | Mit Hilfe eines Assistenten wird die Datenbank abgefragt und entsprechend der Datenbankstruktur ein passendes Modell erstellt. Dieses wird mit einem T4-Skript in die entsprechenden Klassen umgewandelt. |
Architektur

DbContext API[EF4 1] (EF5) | EF4 | Aufgabe |
---|---|---|
DbContext | ObjectContext | Stellt eine Verbindung mit der Datenbank dar. Stellt Methoden für Abfragen (Query), Änderungsverfolgung (Tracking) und Speichern (Save) bereit. |
DbQuery | ObjectQuery | Stellt Methoden für das Hinzufügen (Add), Anhängen (Attach) und Entfernen (Remove) von Entitäten bereit. |
DbSet | ObjectSet | Erbt von DbQuery/ObjectQuery und stellt die entsprechenden Methoden für Entity-Typen bereit. |
Change Tracker API | ObjectContext.ObjectStateManager | Bietet Methoden, um Änderungen verfolgen zu können. Hierzu gehört das Abfragen des ursprünglichen und des derzeitigen Zustands von Entitäten. |
Validation API | — | Automatische Validierung der Daten im DataLayer. |
Code First Model Building | — | Reflektiert über Code-basierte Klassen, um für diese passende Datenbankobjekte und die zugehörigen Metadaten im Arbeitsspeicher und der Datenbank zu erstellen. |
|
Verwendung
Erstellen eines Mappings mit CodeFirst
public class MyDbEntities : DbContext
{
public IDbSet<Person> Persons { get; set; }
// Fluent Configrations
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().ToTable("People");
modelBuilder.Entity<Person>().Property(p => p.FirstName).IsRequired();
// ...
}
}
[Table("People")]
public class Person
{
[Key]
[Column("PersonId")]
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string FirstName { get; set; }
[Required]
[MaxLength(255)]
public string LastName { get; set; }
// ...
}
Datenbank-Initialisierung
Die Datenkbank wird im Entity Framework durch einen Initializer angelegt. Dieser wird ausgeführt, wenn das erste Entity dem Datenbankkontext hinzugefügt wird.[1]
// setze initialisierer
Database.SetInitializer(new DropCreateDatabaseAlways<MyDbEntities>());
using(var context = new MyDbEntities())
{
var person = new Person(){
FirstName = "William",
LastName = "Adama",
DateOfBirth = DateTime.Now
};
context.Persons.Add(person); // Datenbank-Initialisierer wird ausgeführt
context.SaveChanges();
}
In ASP.NET MVC Projekten kann das InitializeSimpleMembership
-Attribut eingesetzt werden, um eine Initialisierung der Datenbank zu gewährleisten.[2]
Hinzufügen von Entities
using(var context = new MyDbEntities())
{
// Erstelle zwei Personen-Entities
var person1 = new Person(){
FirstName = "William",
LastName = "Adama",
DateOfBirth = DateTime.Now
};
var person2 = new Person(){
FirstName = "Laura",
LastName = "Roslin",
DateOfBirth = DateTime.Now
};
// Erstelle ein Adressen-Entity
var address = new Address(){
Street = "Market Street 70",
City = "Philadelphia",
State = "PA",
Zip = "12345"
};
// Erste Variante
context.Persons.Add(person1);
// Zweite Variante
// mit dem Kontext verlinken und als hinzugefügt markieren.
context.Entry(person2).State = EntityState.Added;
// Dritte Variante
// Das Entity wird an ein bereits vom Kontext beobachtetes Entity gehängt
person1.Address.Add(address);
// Speichere Änderungen am Kontext in der Datenbank
context.SaveChanges();
}
Abfrage von Daten
Abfragen aller Daten aus einem Datensatz:
using(var context = new MyDbEntities())
{
foreach(var person in context.Persons) // entspricht SELECT * FROM [Persons]
{
// führt zusätzliche SQL-Abfragen an die Adresses-Tabelle
// mit einem entsprechenden JOIN aus
foreach(var address in person.Adresses)
{
// ...
}
}
}
Um zu verhindern, dass dieselbe Datenbankabfrage mehrfach ausgeführt wird, kann die ToList()
-Methode verwendet werden:
using(var context = new MyDbEntities())
{
var persons = context.Persons;
// Datenbankabfrage wird ausgeführt und als Liste zurückgegeben
var allPersons = persons.ToList();
// Keine weitere Datenbankabfragen durch Verwendung der Liste
foreach(var person in allPersons) { /* ... */ }
foreach(var person in allPersons) { /* ... */ }
}
Sucht ein bestimmtes Objekt in der Datenbank:
var person = context.Persons.SingleOrDefault(p => p.PersonId == personId);
oder in Comprehension Syntax:
var person = (from p in context.Persons
where p.PersonId == personId
select p).SingleOrDefault();
Methode | Ergebnis |
---|---|
Single() |
Gibt das eine Element zurück, welches die Anfrage liefert. Wirft eine Exception, falls keine oder mehrere Ergebnisse zurückgeliefert werden. |
SingleOrDefault() |
Gibt das eine Element zurück, welches die Anfrage liefert. Gibt null zurück, falls keine Ergebnisse geliefert werden. Wirft eine Exception, falls mehrere Ergebnisse zurückgeliefert werden. |
First() |
Gibt das erste Element zurück, falls die Anfrage ein oder mehrere Ergebnisse liefert. Wirft eine Exception, falls keine Ergebnisse zurückgeliefert werden. |
FirstOrDefault() |
Gibt das erste Element zurück, falls die Anfrage ein oder mehrere Ergebnisse liefert. Gibt null zurück, falls keine Ergebnisse zurückgeliefert werden.
|
Bei Db-Objekten steht zudem die Find()
-Methode bereit. Diese sucht das Objekt zuerst im Arbeitsspeicher und macht eine Datenbankabfrage, falls das Objekt nicht im Speicher gefunden wird:
var person = context.Persons.Find(personId);
Lazy, Eager und Explicit Loading
Das Entity Framework verwendet im Normalfall das lazy loading, bei dem Daten aus der Datenbank dann geladen werden, wenn die Daten abgefragt werden:
// lazy loading
// nur die Personen werden abgefragt und geladen
var persons = context.Peoples;
Falls weitere Daten mit geladen werden müssen, wird das eager loading verwendet:
// eager loading
// Adressen werden bereits bei der Abfrage der Personen geladen
var persons = context.Peoples.Include("Addresses");
// LINQ-to-Entities Beispiel für eager loading
var persons = context.Peoples.Include("Addresses").Where(p => p.FirstName == fname);
oder typsicher ab EF5:
// LINQ-to-Entities Beispiel für eager loading
var persons = context.Peoples.Include(p => p.Addresses).Where(p => p.FirstName == fname);
Das explizite Laden (explicit loading) der Daten ist ähnlich dem lazy loading, erlaubt jedoch das Laden der Navigationseigenschaften (navigation properties).
// explicit loading
var persons = context.Peoples; // wie lazy loading; Adressen werden nicht mitgeladen
foreach(var person in persons)
{
person.Addresses.Load(); // explicit loading; navigation properties für Adressen werden geladen
foreach(var address in person.Addresses)
{
// ...
}
}
Delete Entities
Löschen eines Entities im Entity Framework 4:
using(var context = new MyDbEntities())
{
// Abfrage eines Entities aus der Datenbank
var person = context.Persons.SingleOrDefault(p => p.Id == id);
if(person == null)
return;
context.Persons.DeleteObject(person);
context.SaveChanges();
}
Löschen eines Entities im Entity Framework 5:
using(var context = new MyDbEntities())
{
// Abfrage eines Entities aus der Datenbank
var person = (for p in context.Persons.SingleOrDefault(p => p.Id == id);
if(person == null)
return;
context.Entry(person).State = EntityState.Deleted; // Entity zur Löschung markieren
context.SaveChanges(); // Entity in der Datenbank löschen
}
Vorkompilierte Abfragen
Datenbankanfragen werden vom Entity Framework in für die Datenbankschnittstelle passende Anfragen kompiliert. Dieser Vorgang kostet jedoch Zeit, weshalb kompilierte Anfragen – sofern diese erneut benötigt werden – nicht verworfen, sondern in einem Objekt gespeichert und später wiederverwendet werden sollten.
Um bei der ersten Verwendung einer Abfrage mit dem Entity Framework 4 Zeit zu sparen, können Abfragen vorkompiliert werden.
static Func<MyDbEntities, int, ObjectQuery<Person>> QueryContactById =
CompiledQuery.Compile<MyDbEntities, int, IQueryable<Person>>( (context, personId) => context.Persons.Select(p => p.Id == personId) );
Im Entity Framework 5 werden Abfragen automatisch bei der Erstellung vorkompiliert.
Transaktionen
using (var context = new MyDbEntities())
{
using (var scope = new TransactionScope())
{
// Der TransactionScope sucht den neuesten Context auf dem
// Stacktrace und verlinkt sich automatisch mit diesem
try
{
// Bearbeitung von Entities
context.SaveChanges(); // speichern der Änderungen in der Datenbank
scope.Complete(); // Transaktion wird abgeschlossen
}
catch (InvalidOperationException e)
{
// Transaktion fehlgeschlagen
}
} // scope.Dispose()
} // context.Dispose()
Code First Migrations
Code First Migrations ist eine Reihe von Powershell-Skripten, welche die Datenbankmigration erleichtern.
- Enable-Migrations
- erstellt ein Migrations-Skript
- Add-Migration
- Erstelle ein Skript zur Herstellung der aktuellen Datenbankstruktur
- Update-Database
- Bringt die Datenbankstruktur auf einen bestimmten Zustand. Standardmäßig wird das neueste Migrationsskript verwendet.
Literatur
- Andrew Troelsen: Pro C# 5.0 and the .Net 4.5 Framework. Springer, 2012, ISBN 978-1-4302-4233-8, S. 1560 (englisch).
- Julia Lerman, Rowan Miller: Programming Entity Framework: DbContext; Querying, Changing, and Validating Your Data with Entity Framework. O'Reilly Media, 2012, ISBN 978-1-4493-1296-1, S. 258 (englisch).
- Julia Lerman, Rowan Miller: Programming Entity Framework: Code First; Creating and Configuring Data Models from Your Classes. O'Reilly Media, 2012, ISBN 978-1-4493-1294-7, S. 194 (englisch).
Weblinks
- Microsoft .NET Entity Framework. In: CodePlex. Microsoft, abgerufen am 30. Mai 2014 (englisch, Projektseite mit Quellcode des Entity Frameworks).
- Entity Framework Tutorial. Abgerufen am 30. Mai 2014 (englisch).
- Liste von ADO.NET Entity Framework Providern. Abgerufen am 11. November 2012 (englisch).
- Performance Considerations for Entity Framework 5. Abgerufen am 11. November 2012 (englisch).
- Get Started with Entity Framework (EF). In: Data Developer Center. Microsoft, abgerufen am 12. November 2012 (englisch).
- Entity Framework Power Tools. In: Data Developer Center. Microsoft, abgerufen am 30. März 2013 (englisch).
Referenzen
- ↑ Bipin Joshi: Understanding Database Initializers in Entity Framework Code First. 10. Februar 2012, abgerufen am 10. Juni 2014 (englisch).
- ↑ Rick Anderson: InitializeSimpleMembership Attribute and SimpleMembership Exceptions. In: MSDN Blog. Microsoft, 15. August 2012, abgerufen am 10. Juni 2014 (englisch).