Entity Framework
| ADO.NET Entity Framework (bis ca. 2012), Entity Framework | ||
|---|---|---|
| Basisdaten | ||
| Hauptentwickler | GitHub | |
| Entwickler | Microsoft (ursprünglich) | |
| Erscheinungsjahr | 2008 | |
| Aktuelle Version | 6.4.4 (15. Mai 2020) | |
| Programmiersprache | C# | |
| Kategorie | ORM-Framework | |
| Lizenz | Apache 2.0 | |
| EF auf GitHub | ||
| Entity Framework Core auch: EF Core | ||
|---|---|---|
| Basisdaten | ||
| Hauptentwickler | GitHub | |
| Entwickler | Microsoft | |
| Erscheinungsjahr | 2016 | |
| Aktuelle Version | 6.0.8[1] (August 2022) | |
| Aktuelle Vorabversion | 7.0 Preview 7[1] (August 2022) | |
| Programmiersprache | C# | |
| Kategorie | ORM-Framework | |
| Lizenz | Apache 2.0 | |
| EF Core auf GitHub | ||
| 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
[Bearbeiten | Quelltext bearbeiten]
| 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
[Bearbeiten | Quelltext bearbeiten]Erstellen eines Mappings mit CodeFirst
[Bearbeiten | Quelltext bearbeiten]public class MyDbEntities : DbContext
{
public IDbSet<Person> Persons { get; set; }
// Fluent Configurations
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
[Bearbeiten | Quelltext bearbeiten]Die Datenbank wird im Entity Framework durch einen Initializer angelegt. Dieser wird ausgeführt, wenn das erste Entity dem Datenbankkontext hinzugefügt wird.[5]
// 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.[6]
Hinzufügen von Entities
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]Das Entity Framework verwendet im Normalfall lazy loading, bei dem Daten aus der Datenbank erst dann geladen werden, wenn die Daten abgefragt werden:
// lazy loading
// nur die Personen werden abgefragt und geladen
var persons = context.Peoples;
Falls weitere Daten mitgeladen werden müssen, wird 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
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]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
[Bearbeiten | Quelltext bearbeiten]- Andrew Troelsen: Pro C# 5.0 and the .Net 4.5 Framework. Springer, 2012, ISBN 978-1-4302-4233-8, S. 1560.
- 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.
- 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.
Weblinks
[Bearbeiten | Quelltext bearbeiten]- Microsoft .NET Entity Framework. In: CodePlex. Microsoft, abgerufen am 12. März 2015 (englisch, Projektseite mit Quellcode des Entity Frameworks).
- Entity Framework Tutorial. Abgerufen am 12. März 2015 (englisch).
- Entity Framework Grundlagen Video. Abgerufen am 15. März 2015.
- 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).
Einzelnachweise
[Bearbeiten | Quelltext bearbeiten]- 1 2 Releases, in: GitHub, abgerufen am 30. Aug. 2022
- ↑ Holger Schwichtenberg: Microsoft stellt OR-Mapper von .NET quelloffen zur Verfügung, in: heise online vom 19. Juli 2012, abgerufen am 7. Dez. 2015.
- ↑ Holger Schwichtenberg: Asynchrone Datenbankzugriffe mit Microsofts Entity Framework 6.0, in: heise online vom 18. Okt. 2013, abgerufen am 7. Dez. 2015.
- ↑ Announcing Entity Framework Core 3.1 and Entity Framework 6.4 – .NET Blog. Abgerufen am 7. Dezember 2019.
- ↑ 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).