<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://dev.kaibel.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PhilKa</id>
	<title>dev.kaibel.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://dev.kaibel.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PhilKa"/>
	<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Spezial:Beitr%C3%A4ge/PhilKa"/>
	<updated>2026-10-25T01:06:27Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=235</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=235"/>
		<updated>2026-09-09T13:21:57Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* EF Core gegenüber ADO.NET */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ===&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Strategien zum Laden von Beziehungen&lt;br /&gt;
|-&lt;br /&gt;
! Strategie !! Beschreibung &lt;br /&gt;
|-                                   &lt;br /&gt;
| Eager Loading                          || zusammengehörige Daten werden sofort mitgeladen                    &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| Explicit Loading                       || zusammengehörige Daten werden gezielt nachgeladen                  &lt;br /&gt;
|-                      &lt;br /&gt;
| Lazy Loading                           || Daten werden erst beim Zugriff auf die Navigation Property geladen &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen&lt;br /&gt;
|-&lt;br /&gt;
! Befehl !! Funktion                         &lt;br /&gt;
|-        &lt;br /&gt;
| dotnet ef migrations add Name              || neue Migration erzeugen                           &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations list                  || vorhandene Migrationen anzeigen                   &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations remove                || letzte Migration entfernen                        &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update                  || Datenbank auf die neueste Migration aktualisieren &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update MigrationName    || Datenbank auf eine bestimmte Migration setzen     &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations script                || SQL-Skript für Migrationen erzeugen               &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich von Code First und Database First&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! Code First !! Database First&lt;br /&gt;
|-           &lt;br /&gt;
| Ausgangspunkt                                 || C#-Modell                    || vorhandene Datenbank        &lt;br /&gt;
|-                                     &lt;br /&gt;
| Schemaerstellung                              || über EF Core und Migrationen || Datenbank existiert bereits &lt;br /&gt;
|-                                          &lt;br /&gt;
| Geeignet für neue Anwendungen                 || sehr gut                     || eingeschränkt               &lt;br /&gt;
|-                                         &lt;br /&gt;
| Geeignet für Legacy-Datenbanken               || möglich                      || sehr gut                    &lt;br /&gt;
|-                                        &lt;br /&gt;
| Schemaänderungen                              || häufig über Migrationen      || häufig datenbankseitig      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zusammenhang zwischen Entity State und Datenbankoperation &lt;br /&gt;
|-&lt;br /&gt;
! Entity State !! Typische Datenbankoperation &lt;br /&gt;
|-                       &lt;br /&gt;
| Added           || INSERT         &lt;br /&gt;
|-                                             &lt;br /&gt;
| Modified        || UPDATE         &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Deleted         || DELETE         &lt;br /&gt;
|-                                                     &lt;br /&gt;
| Unchanged       || keine Änderung &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung von EF-Core-Komponenten zu bekannten Patterns&lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Komponente !! Ähnliches Entwurfsmuster&lt;br /&gt;
|-                                                      &lt;br /&gt;
| DbSet        || Repository   &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| DbContext    || Unit of Work &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=234</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=234"/>
		<updated>2026-09-09T10:25:45Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ===&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Strategien zum Laden von Beziehungen&lt;br /&gt;
|-&lt;br /&gt;
! Strategie !! Beschreibung &lt;br /&gt;
|-                                   &lt;br /&gt;
| Eager Loading                          || zusammengehörige Daten werden sofort mitgeladen                    &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| Explicit Loading                       || zusammengehörige Daten werden gezielt nachgeladen                  &lt;br /&gt;
|-                      &lt;br /&gt;
| Lazy Loading                           || Daten werden erst beim Zugriff auf die Navigation Property geladen &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen&lt;br /&gt;
|-&lt;br /&gt;
! Befehl !! Funktion                         &lt;br /&gt;
|-        &lt;br /&gt;
| dotnet ef migrations add Name              || neue Migration erzeugen                           &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations list                  || vorhandene Migrationen anzeigen                   &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations remove                || letzte Migration entfernen                        &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update                  || Datenbank auf die neueste Migration aktualisieren &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update MigrationName    || Datenbank auf eine bestimmte Migration setzen     &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations script                || SQL-Skript für Migrationen erzeugen               &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich von Code First und Database First&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! Code First !! Database First&lt;br /&gt;
|-           &lt;br /&gt;
| Ausgangspunkt                                 || C#-Modell                    || vorhandene Datenbank        &lt;br /&gt;
|-                                     &lt;br /&gt;
| Schemaerstellung                              || über EF Core und Migrationen || Datenbank existiert bereits &lt;br /&gt;
|-                                          &lt;br /&gt;
| Geeignet für neue Anwendungen                 || sehr gut                     || eingeschränkt               &lt;br /&gt;
|-                                         &lt;br /&gt;
| Geeignet für Legacy-Datenbanken               || möglich                      || sehr gut                    &lt;br /&gt;
|-                                        &lt;br /&gt;
| Schemaänderungen                              || häufig über Migrationen      || häufig datenbankseitig      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zusammenhang zwischen Entity State und Datenbankoperation &lt;br /&gt;
|-&lt;br /&gt;
! Entity State !! Typische Datenbankoperation &lt;br /&gt;
|-                       &lt;br /&gt;
| Added           || INSERT         &lt;br /&gt;
|-                                             &lt;br /&gt;
| Modified        || UPDATE         &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Deleted         || DELETE         &lt;br /&gt;
|-                                                     &lt;br /&gt;
| Unchanged       || keine Änderung &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung von EF-Core-Komponenten zu bekannten Patterns&lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Komponente !! Ähnliches Entwurfsmuster&lt;br /&gt;
|-                                                      &lt;br /&gt;
| DbSet        || Repository   &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| DbContext    || Unit of Work &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=233</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=233"/>
		<updated>2026-09-09T10:18:17Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Code First und Database First im Vergleich */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Strategien zum Laden von Beziehungen&lt;br /&gt;
|-&lt;br /&gt;
! Strategie !! Beschreibung &lt;br /&gt;
|-                                   &lt;br /&gt;
| Eager Loading                          || zusammengehörige Daten werden sofort mitgeladen                    &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| Explicit Loading                       || zusammengehörige Daten werden gezielt nachgeladen                  &lt;br /&gt;
|-                      &lt;br /&gt;
| Lazy Loading                           || Daten werden erst beim Zugriff auf die Navigation Property geladen &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen&lt;br /&gt;
|-&lt;br /&gt;
! Befehl !! Funktion                         &lt;br /&gt;
|-        &lt;br /&gt;
| dotnet ef migrations add Name              || neue Migration erzeugen                           &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations list                  || vorhandene Migrationen anzeigen                   &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations remove                || letzte Migration entfernen                        &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update                  || Datenbank auf die neueste Migration aktualisieren &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update MigrationName    || Datenbank auf eine bestimmte Migration setzen     &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations script                || SQL-Skript für Migrationen erzeugen               &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich von Code First und Database First&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! Code First !! Database First&lt;br /&gt;
|-           &lt;br /&gt;
| Ausgangspunkt                                 || C#-Modell                    || vorhandene Datenbank        &lt;br /&gt;
|-                                     &lt;br /&gt;
| Schemaerstellung                              || über EF Core und Migrationen || Datenbank existiert bereits &lt;br /&gt;
|-                                          &lt;br /&gt;
| Geeignet für neue Anwendungen                 || sehr gut                     || eingeschränkt               &lt;br /&gt;
|-                                         &lt;br /&gt;
| Geeignet für Legacy-Datenbanken               || möglich                      || sehr gut                    &lt;br /&gt;
|-                                        &lt;br /&gt;
| Schemaänderungen                              || häufig über Migrationen      || häufig datenbankseitig      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=232</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=232"/>
		<updated>2026-09-09T10:15:59Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Wichtige Migrationsbefehle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Strategien zum Laden von Beziehungen&lt;br /&gt;
|-&lt;br /&gt;
! Strategie !! Beschreibung &lt;br /&gt;
|-                                   &lt;br /&gt;
| Eager Loading                          || zusammengehörige Daten werden sofort mitgeladen                    &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| Explicit Loading                       || zusammengehörige Daten werden gezielt nachgeladen                  &lt;br /&gt;
|-                      &lt;br /&gt;
| Lazy Loading                           || Daten werden erst beim Zugriff auf die Navigation Property geladen &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen&lt;br /&gt;
|-&lt;br /&gt;
! Befehl !! Funktion                         &lt;br /&gt;
|-        &lt;br /&gt;
| dotnet ef migrations add Name              || neue Migration erzeugen                           &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations list                  || vorhandene Migrationen anzeigen                   &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations remove                || letzte Migration entfernen                        &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update                  || Datenbank auf die neueste Migration aktualisieren &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef database update MigrationName    || Datenbank auf eine bestimmte Migration setzen     &lt;br /&gt;
|-                                                                                             &lt;br /&gt;
| dotnet ef migrations script                || SQL-Skript für Migrationen erzeugen               &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=231</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=231"/>
		<updated>2026-09-09T10:14:03Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Laden zusammengehöriger Daten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Strategien zum Laden von Beziehungen&lt;br /&gt;
|-&lt;br /&gt;
! Strategie !! Beschreibung &lt;br /&gt;
|-                                   &lt;br /&gt;
| Eager Loading                          || zusammengehörige Daten werden sofort mitgeladen                    &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| Explicit Loading                       || zusammengehörige Daten werden gezielt nachgeladen                  &lt;br /&gt;
|-                      &lt;br /&gt;
| Lazy Loading                           || Daten werden erst beim Zugriff auf die Navigation Property geladen &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=230</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=230"/>
		<updated>2026-09-09T10:09:44Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Beziehungen zwischen Entities */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beziehungstypen                             &lt;br /&gt;
|-&lt;br /&gt;
! Beziehung !! Beispiel                            &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:1                     || Benutzer und Benutzerprofil &lt;br /&gt;
|-                                                    &lt;br /&gt;
| 1:n                     || Kunde und Bestellungen      &lt;br /&gt;
|-                                                    &lt;br /&gt;
| n:m                     || Produkte und Kategorien     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=229</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=229"/>
		<updated>2026-09-09T10:08:14Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Data Annotations */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Data Annotations&lt;br /&gt;
|-&lt;br /&gt;
! Attribut !! Bedeutung         &lt;br /&gt;
|-                 &lt;br /&gt;
| [Key]                                || definiert einen Primärschlüssel                             &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Required]                           || markiert ein Pflichtfeld                                    &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [MaxLength]                          || definiert eine maximale Länge                               &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Column]                             || konfiguriert eine Datenbankspalte                           &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [Table]                              || konfiguriert den Tabellennamen                              &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [NotMapped]                          || Property wird nicht in der Datenbank gespeichert            &lt;br /&gt;
|-                                                                                                 &lt;br /&gt;
| [ForeignKey]                         || definiert beziehungsweise kennzeichnet einen Fremdschlüssel &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=228</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=228"/>
		<updated>2026-09-09T10:04:05Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Möglichkeiten zum Testen von EF-Core-Anwendungen&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Eigenschaften                                                                      &lt;br /&gt;
|-                           &lt;br /&gt;
| echte Testdatenbank                                || sehr realitätsnah                                                      &lt;br /&gt;
|-                                  &lt;br /&gt;
| temporäre Datenbank                                || gut für automatisierte Integrationstests                               &lt;br /&gt;
|-                                       &lt;br /&gt;
| SQLite In-Memory                                   || relationale Datenbank im Arbeitsspeicher                               &lt;br /&gt;
|-                                 &lt;br /&gt;
| EF-Core-InMemory-Provider                          || schnell, aber kein vollständiger Ersatz für eine relationale Datenbank &lt;br /&gt;
|-                              &lt;br /&gt;
| Testcontainer                                      || echtes Datenbanksystem innerhalb eines Containers                      &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vor- und Nachteile von Soft Delete&lt;br /&gt;
|-&lt;br /&gt;
! Vorteile !! Nachteile          &lt;br /&gt;
|-  &lt;br /&gt;
| Daten können wiederhergestellt werden || Datenbank wächst dauerhaft weiter                      &lt;br /&gt;
|-                &lt;br /&gt;
| Historie bleibt erhalten              || Abfragen müssen gelöschte Datensätze berücksichtigen   &lt;br /&gt;
|-                   &lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   || Unique Constraints können komplizierter werden         &lt;br /&gt;
|-                       &lt;br /&gt;
| Auditing wird erleichtert             || Datenschutz kann trotzdem physische Löschung erfordern &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufige EF-Core-Probleme  &lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Typische Ursache     &lt;br /&gt;
|-                                  &lt;br /&gt;
| DbContext wurde bereits disposed               || DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          &lt;br /&gt;
|-                                      &lt;br /&gt;
| A second operation was started on this context || mehrere parallele Operationen verwenden denselben DbContext                 &lt;br /&gt;
|-                                &lt;br /&gt;
| Entity wird bereits getrackt                   || mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt &lt;br /&gt;
|-                                     &lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      || verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       &lt;br /&gt;
|-                            &lt;br /&gt;
| Migration passt nicht zur Datenbank            || Schema und Migrationshistorie sind nicht synchron                           &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und ADO.NET&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! ADO.NET               &lt;br /&gt;
|-                          &lt;br /&gt;
| Abstraktionsgrad                                       || hoch               || niedrig                       &lt;br /&gt;
|-                                              &lt;br /&gt;
| Abfragesprache                                         || hauptsächlich LINQ || hauptsächlich SQL             &lt;br /&gt;
|-                                   &lt;br /&gt;
| Objekt-Mapping                                         || automatisch        || normalerweise manuell         &lt;br /&gt;
|-                                 &lt;br /&gt;
| Change Tracking                                        || integriert         || normalerweise manuell         &lt;br /&gt;
|-                                              &lt;br /&gt;
| Migrationen                                            || integriert         || nicht Bestandteil von ADO.NET &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Kontrolle über SQL                                     || geringer           || sehr hoch                     &lt;br /&gt;
|-                                                                   &lt;br /&gt;
| Entwicklungsaufwand                                    || häufig geringer    || häufig höher                  &lt;br /&gt;
|}                                            &lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Vergleich zwischen Entity Framework Core und Dappe&lt;br /&gt;
|-&lt;br /&gt;
! Eigenschaft !! EF Core !! Dapper            &lt;br /&gt;
|-                                                  &lt;br /&gt;
| LINQ-Abfragen                                         || Ja                         || normalerweise Nein     &lt;br /&gt;
|-                                                            &lt;br /&gt;
| automatische SQL-Erzeugung                            || Ja                         || überwiegend Nein       &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Change Tracking                                       || Ja                         || Nein                   &lt;br /&gt;
|-                                                         &lt;br /&gt;
| Migrationen                                           || Ja                         || Nein                   &lt;br /&gt;
|-                                                                 &lt;br /&gt;
| Beziehungen                                           || umfangreiche Unterstützung || weitgehend manuell     &lt;br /&gt;
|-                                                          &lt;br /&gt;
| Abstraktionsgrad                                      || hoch                       || niedrig                &lt;br /&gt;
|-                                                        &lt;br /&gt;
| Kontrolle über SQL                                    || mittel bis hoch            || sehr hoch              &lt;br /&gt;
|-                                         &lt;br /&gt;
| CRUD-Entwicklung                                      || sehr komfortabel           || stärker SQL-orientiert &lt;br /&gt;
|}         &lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Aktuell relevante EF-Core-Versionen  &lt;br /&gt;
|-&lt;br /&gt;
! EF-Core-Version !! Zielframework !! Support &lt;br /&gt;
|-                                    &lt;br /&gt;
| EF Core 10                                    || .NET 10 || bis 10. November 2028 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 9                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|-                                      &lt;br /&gt;
| EF Core 8                                     || .NET 8  || bis 10. November 2026 &lt;br /&gt;
|}                                &lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=227</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=227"/>
		<updated>2026-09-09T09:54:27Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Fluent API */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=226</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=226"/>
		<updated>2026-09-09T09:53:43Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Konfiguration des Datenmodells */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|+ Möglichkeiten zur Konfiguration eines EF-Core-Modells&lt;br /&gt;
|-&lt;br /&gt;
! Verfahren !! Beschreibung                                                                      &lt;br /&gt;
|-                                            &lt;br /&gt;
| Conventions                                             || automatische Erkennung anhand von Namens- und Strukturkonventionen &lt;br /&gt;
|-                                                    &lt;br /&gt;
| Data Annotations                                        || Konfiguration über Attribute an C#-Klassen und Properties          &lt;br /&gt;
|-                                                                                                                 &lt;br /&gt;
| Fluent API                                              || Konfiguration über C#-Methoden im ModelBuilder                     &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=225</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=225"/>
		<updated>2026-09-09T09:52:05Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Entity States */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zustände einer Entity im Change Tracker&lt;br /&gt;
|-&lt;br /&gt;
! Zustand !! Bedeutung &lt;br /&gt;
|-&lt;br /&gt;
| Detached                                  || Entity wird vom DbContext nicht verfolgt         &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Unchanged                                 || Entity wird verfolgt, wurde aber nicht verändert &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Added                                     || Entity soll neu eingefügt werden                 &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Modified                                  || Entity wurde verändert                           &lt;br /&gt;
|-                                                                                           &lt;br /&gt;
| Deleted                                   || Entity soll gelöscht werden                      &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=224</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=224"/>
		<updated>2026-09-09T09:41:24Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Asynchrone Datenbankzugriffe */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiele synchroner und asynchroner EF-Core-Methoden &lt;br /&gt;
|-&lt;br /&gt;
! Synchron !! Asynchron &lt;br /&gt;
|-                                                          &lt;br /&gt;
| ToList()                                                || ToListAsync()  &lt;br /&gt;
|-                                                   &lt;br /&gt;
| First()                                                 || FirstAsync()         &lt;br /&gt;
|-                                                                &lt;br /&gt;
| FirstOrDefault()                                        || FirstOrDefaultAsync() &lt;br /&gt;
|-                                                                    &lt;br /&gt;
| Single()                                                || SingleAsync()         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Count()                                                 || CountAsync()          &lt;br /&gt;
|-                                                                &lt;br /&gt;
| SaveChanges()                                           || SaveChangesAsync()    &lt;br /&gt;
|}                          &lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=223</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=223"/>
		<updated>2026-09-09T09:39:21Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Datenbankprovider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
|-                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
|-                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
|-                                                       &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
|-                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           || Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
|-                                                                                 &lt;br /&gt;
| PostgreSQL                            || Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
|-                                                                &lt;br /&gt;
| MySQL / MariaDB                       || beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=222</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=222"/>
		<updated>2026-09-09T09:37:21Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Object-Relational Mapping */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=221</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=221"/>
		<updated>2026-09-09T09:35:35Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Wichtige LINQ-Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
|-                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=220</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=220"/>
		<updated>2026-09-09T09:35:06Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* = Wichtige LINQ-Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
|-  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
|-      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
|-             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
|-   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
|-     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
|- &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
|-                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
|-                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
|-                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
|-                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
|-                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
|-                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
| -                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
|-                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
|-                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=219</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=219"/>
		<updated>2026-09-09T09:33:59Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* = Wichtige LINQ-Methoden */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Häufig verwendete LINQ-Methoden mit EF Core&lt;br /&gt;
|-&lt;br /&gt;
! Methode !! Funktion&lt;br /&gt;
| -  &lt;br /&gt;
| Where()                                       || Datensätze filtern                               &lt;br /&gt;
| -      &lt;br /&gt;
| Select()                                      || Daten beziehungsweise Properties auswählen       &lt;br /&gt;
| -             &lt;br /&gt;
| OrderBy()                                     || aufsteigend sortieren                            &lt;br /&gt;
| -   &lt;br /&gt;
| OrderByDescending()                           || absteigend sortieren                             &lt;br /&gt;
| -     &lt;br /&gt;
| ThenBy()                                      || zusätzliche Sortierung definieren                &lt;br /&gt;
| - &lt;br /&gt;
| FirstAsync()                                  || ersten Datensatz zurückgeben                     &lt;br /&gt;
| -                                        &lt;br /&gt;
| FirstOrDefaultAsync()                         || ersten Datensatz oder Standardwert zurückgeben   &lt;br /&gt;
| -                                         &lt;br /&gt;
| SingleAsync()                                 || genau einen Datensatz erwarten                   &lt;br /&gt;
| -                                         &lt;br /&gt;
| SingleOrDefaultAsync()                        || genau einen Datensatz oder Standardwert erwarten &lt;br /&gt;
| -                                         &lt;br /&gt;
| AnyAsync()                                    || prüfen, ob mindestens ein Datensatz existiert    &lt;br /&gt;
| -                                       &lt;br /&gt;
| CountAsync()                                  || Datensätze zählen                                &lt;br /&gt;
| -                                      &lt;br /&gt;
| SumAsync()                                    || Summe berechnen                                  &lt;br /&gt;
| -                                        &lt;br /&gt;
| AverageAsync()                                || Durchschnitt berechnen                           &lt;br /&gt;
| -                                          &lt;br /&gt;
| MinAsync()                                    || Minimalwert bestimmen                            &lt;br /&gt;
| -                                     &lt;br /&gt;
| MaxAsync()                                    || Maximalwert bestimmen                            &lt;br /&gt;
| -                                          &lt;br /&gt;
| ToListAsync()                                 || Ergebnis als Liste laden                         &lt;br /&gt;
| }&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=218</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=218"/>
		<updated>2026-09-09T09:31:13Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* CRUD-Operationen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
|-       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
|-                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
|-        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
|-                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
|}     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=217</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=217"/>
		<updated>2026-09-09T09:25:57Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* CRUD-Operationen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ CRUD-Operationen&lt;br /&gt;
|-&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung&lt;br /&gt;
| -       &lt;br /&gt;
| Create                                       || Datensatz anlegen || INSERT &lt;br /&gt;
| -                              &lt;br /&gt;
| Read                                         || Datensatz lesen   || SELECT &lt;br /&gt;
| -        &lt;br /&gt;
| Update                                       || Datensatz ändern  || UPDATE &lt;br /&gt;
| -                             &lt;br /&gt;
| Delete                                       || Datensatz löschen || DELETE &lt;br /&gt;
| }     &lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=216</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=216"/>
		<updated>2026-09-09T09:22:32Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Datenbankprovider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                             &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      || Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                                                                   &lt;br /&gt;
| SQLite                                || Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     ||                                                 &lt;br /&gt;
| Azure Cosmos DB                       || Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                                                               &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                                                                &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=215</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=215"/>
		<updated>2026-09-09T09:21:21Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Datenbankprovider */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Häufig verwendete Datenbankprovider&lt;br /&gt;
|-&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket&lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| Microsoft SQL Server / Azure SQL      |   | Microsoft.EntityFrameworkCore.SqlServer         &lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| SQLite                                |   | Microsoft.EntityFrameworkCore.Sqlite            &lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| Azure Cosmos DB                       |   | Microsoft.EntityFrameworkCore.Cosmos            &lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          &lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           &lt;br /&gt;
| -                                     |   |                                                 &lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql &lt;br /&gt;
|}                                  &lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=214</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=214"/>
		<updated>2026-09-09T09:17:53Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Object-Relational Mapping */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank &lt;br /&gt;
|-&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Klasse     || Tabelle&lt;br /&gt;
|-                                                         &lt;br /&gt;
| Objekt      || Datensatz / Tabellenzeile&lt;br /&gt;
|-                                                &lt;br /&gt;
| Property   || Spalte&lt;br /&gt;
|-                                               &lt;br /&gt;
| Objekt-ID  || Primärschlüssel&lt;br /&gt;
|-                                            &lt;br /&gt;
| Referenz auf ein Objekt || Fremdschlüssel&lt;br /&gt;
|-                                  &lt;br /&gt;
| Collection  || Beziehung zu mehreren Datensätzen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
|+ Beispiel für die Tabelle Products &lt;br /&gt;
|-&lt;br /&gt;
! Id !! Name !! Price&lt;br /&gt;
|-&lt;br /&gt;
| 1  || Kalkammonsalpeter || 289,00&lt;br /&gt;
|-&lt;br /&gt;
| 2   || Harnstoff         || 315,00&lt;br /&gt;
|-&lt;br /&gt;
| 3  || Kali 60   || 245,00&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Datenbankprovider |   |                                                 |&lt;br /&gt;
| ------------------------------------- | - | ----------------------------------------------- |&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket      |   |                                                 |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Microsoft SQL Server / Azure SQL      |   | Microsoft.EntityFrameworkCore.SqlServer         |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| SQLite                                |   | Microsoft.EntityFrameworkCore.Sqlite            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Azure Cosmos DB                       |   | Microsoft.EntityFrameworkCore.Cosmos            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql |&lt;br /&gt;
| }                                     |   |                                                 |&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=213</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=213"/>
		<updated>2026-09-09T09:12:22Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Object-Relational Mapping */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank |   |                                   |&lt;br /&gt;
| ------------------------------------------------------------ | - | --------------------------------- |&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank       |   |                                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Klasse                                                       |   | Tabelle                           |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt                                                       |   | Datensatz / Tabellenzeile         |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Property                                                     |   | Spalte                            |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt-ID                                                    |   | Primärschlüssel                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Referenz auf ein Objekt                                      |   | Fremdschlüssel                    |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Collection                                                   |   | Beziehung zu mehreren Datensätzen |&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiel für die Tabelle Products |   |                   |   |        |&lt;br /&gt;
| ----------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Id !! Name !! Price               |   |                   |   |        |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 1                                   |   | Kalkammonsalpeter |   | 289,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 2                                   |   | Harnstoff         |   | 315,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 3                                   |   | Kali 60           |   | 245,00 |&lt;br /&gt;
| }                                   |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Datenbankprovider |   |                                                 |&lt;br /&gt;
| ------------------------------------- | - | ----------------------------------------------- |&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket      |   |                                                 |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Microsoft SQL Server / Azure SQL      |   | Microsoft.EntityFrameworkCore.SqlServer         |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| SQLite                                |   | Microsoft.EntityFrameworkCore.Sqlite            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Azure Cosmos DB                       |   | Microsoft.EntityFrameworkCore.Cosmos            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql |&lt;br /&gt;
| }                                     |   |                                                 |&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=212</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=212"/>
		<updated>2026-09-09T09:08:47Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank |   |                                   |&lt;br /&gt;
| ------------------------------------------------------------ | - | --------------------------------- |&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank       |   |                                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Klasse                                                       |   | Tabelle                           |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt                                                       |   | Datensatz / Tabellenzeile         |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Property                                                     |   | Spalte                            |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt-ID                                                    |   | Primärschlüssel                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Referenz auf ein Objekt                                      |   | Fremdschlüssel                    |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Collection                                                   |   | Beziehung zu mehreren Datensätzen |&lt;br /&gt;
| }                                                            |   |                                   |&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiel für die Tabelle Products |   |                   |   |        |&lt;br /&gt;
| ----------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Id !! Name !! Price               |   |                   |   |        |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 1                                   |   | Kalkammonsalpeter |   | 289,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 2                                   |   | Harnstoff         |   | 315,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 3                                   |   | Kali 60           |   | 245,00 |&lt;br /&gt;
| }                                   |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Datenbankprovider |   |                                                 |&lt;br /&gt;
| ------------------------------------- | - | ----------------------------------------------- |&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket      |   |                                                 |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Microsoft SQL Server / Azure SQL      |   | Microsoft.EntityFrameworkCore.SqlServer         |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| SQLite                                |   | Microsoft.EntityFrameworkCore.Sqlite            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Azure Cosmos DB                       |   | Microsoft.EntityFrameworkCore.Cosmos            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql |&lt;br /&gt;
| }                                     |   |                                                 |&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=211</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=211"/>
		<updated>2026-09-09T09:08:06Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
**TOC**&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Tabellen und Datensätze einer Datenbank auf C#-Klassen und Objekte ab. Datenbankabfragen können dabei überwiegend mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. Entity Framework Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems.&lt;br /&gt;
&lt;br /&gt;
Entity Framework Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039; und wurde speziell für das plattformübergreifende .NET entwickelt.&lt;br /&gt;
&lt;br /&gt;
Mit EF Core können unter anderem Anwendungen für folgende Plattformen entwickelt werden:&lt;br /&gt;
&lt;br /&gt;
* Windows&lt;br /&gt;
* Linux&lt;br /&gt;
* macOS&lt;br /&gt;
* Webserver mit ASP.NET Core&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Cloud-Anwendungen&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039; und basiert auf &#039;&#039;&#039;.NET 10&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ein ORM muss eine Anwendung SQL-Abfragen normalerweise selbst erstellen und die zurückgegebenen Daten anschließend manuell auf Objekte abbilden.&lt;br /&gt;
&lt;br /&gt;
Eine SQL-Abfrage könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze beispielsweise mit ADO.NET gelesen und in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage stattdessen mit LINQ formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese Abfrage in ein für den verwendeten Datenbankanbieter geeignetes SQL-Statement.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Anwendung&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen von Daten funktioniert die Verarbeitung entsprechend in die Gegenrichtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
     │&lt;br /&gt;
     ▼&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein ORM versucht, das objektorientierte Datenmodell einer Anwendung mit dem relationalen Datenmodell einer Datenbank zu verbinden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung zwischen Objektmodell und relationaler Datenbank |   |                                   |&lt;br /&gt;
| ------------------------------------------------------------ | - | --------------------------------- |&lt;br /&gt;
| ! Objektorientierte Anwendung !! Relationale Datenbank       |   |                                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Klasse                                                       |   | Tabelle                           |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt                                                       |   | Datensatz / Tabellenzeile         |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Property                                                     |   | Spalte                            |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Objekt-ID                                                    |   | Primärschlüssel                   |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Referenz auf ein Objekt                                      |   | Fremdschlüssel                    |&lt;br /&gt;
| -                                                            |   |                                   |&lt;br /&gt;
| Collection                                                   |   | Beziehung zu mehreren Datensätzen |&lt;br /&gt;
| }                                                            |   |                                   |&lt;br /&gt;
&lt;br /&gt;
Eine einfache C#-Klasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus beispielsweise eine Datenbanktabelle mit folgendem Aufbau erzeugen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiel für die Tabelle Products |   |                   |   |        |&lt;br /&gt;
| ----------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Id !! Name !! Price               |   |                   |   |        |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 1                                   |   | Kalkammonsalpeter |   | 289,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 2                                   |   | Harnstoff         |   | 315,00 |&lt;br /&gt;
| -                                   |   |                   |   |        |&lt;br /&gt;
| 3                                   |   | Kali 60           |   | 245,00 |&lt;br /&gt;
| }                                   |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
== Wichtige Bestandteile von EF Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den zentralen Bestandteilen von Entity Framework Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – C#-Klasse, die Daten eines bestimmten Typs repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – Abfragesprache innerhalb von .NET&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Änderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – stellt die Verbindung zu einem konkreten Datenbanksystem her&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;Query Translation&#039;&#039;&#039; – übersetzt LINQ-Abfragen in SQL beziehungsweise die Abfragesprache des Providers&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; in ein .NET-Projekt eingebunden.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
Das grundlegende EF-Core-Paket kann über die Kommandozeile installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird normalerweise ein Datenbankprovider benötigt.&lt;br /&gt;
&lt;br /&gt;
=== Microsoft SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und verschiedene Entwicklungswerkzeuge wird zusätzlich das Design-Paket benötigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== dotnet-ef installieren ===&lt;br /&gt;
&lt;br /&gt;
Für die Verwaltung von Migrationen über die Kommandozeile kann das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die installierte Version kann überprüft werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef --version&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core selbst enthält keine direkte Implementierung für jedes Datenbanksystem. Stattdessen werden sogenannte &#039;&#039;&#039;Database Provider&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Provider übersetzt EF-Core-Operationen in Befehle für das jeweilige Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Datenbankprovider |   |                                                 |&lt;br /&gt;
| ------------------------------------- | - | ----------------------------------------------- |&lt;br /&gt;
| ! Datenbanksystem !! NuGet-Paket      |   |                                                 |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Microsoft SQL Server / Azure SQL      |   | Microsoft.EntityFrameworkCore.SqlServer         |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| SQLite                                |   | Microsoft.EntityFrameworkCore.Sqlite            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| Azure Cosmos DB                       |   | Microsoft.EntityFrameworkCore.Cosmos            |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| EF-Core-In-Memory-Datenbank           |   | Microsoft.EntityFrameworkCore.InMemory          |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| PostgreSQL                            |   | Npgsql.EntityFrameworkCore.PostgreSQL           |&lt;br /&gt;
| -                                     |   |                                                 |&lt;br /&gt;
| MySQL / MariaDB                       |   | beispielsweise Pomelo.EntityFrameworkCore.MySql |&lt;br /&gt;
| }                                     |   |                                                 |&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss darauf geachtet werden, dass die verwendete Version mit der eingesetzten EF-Core-Hauptversion kompatibel ist.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Entity&#039;&#039;&#039; ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften anhand von Konventionen automatisch.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ wird normalerweise auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel der Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; erkannt.&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext stellt eine Arbeitseinheit beziehungsweise Sitzung mit einer Datenbank dar.&lt;br /&gt;
&lt;br /&gt;
Er ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erstellen neuer Datensätze&lt;br /&gt;
* Ändern vorhandener Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Verwaltung des Datenmodells&lt;br /&gt;
* Ausführen von SaveChanges&lt;br /&gt;
* Verwaltung von Datenbankverbindungen&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DbSet ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities eines bestimmten Typs.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Über dieses DbSet können beispielsweise Produkte abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbSet kann vereinfacht als objektorientierte Darstellung einer Datenbanktabelle betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Dabei ist jedoch zu beachten, dass ein DbSet nicht zwangsläufig exakt einer einzelnen physischen Tabelle entsprechen muss.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Eine Datenbankverbindung wird normalerweise über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; definiert.&lt;br /&gt;
&lt;br /&gt;
Ein Connection String für SQL Server könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In einer ASP.NET-Core-Anwendung wird der Connection String normalerweise in der Datei &amp;lt;code&amp;gt;appsettings.json&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der DbContext in &amp;lt;code&amp;gt;Program.cs&amp;lt;/code&amp;gt; registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core besitzt ein integriertes &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann dort beispielsweise folgendermaßen registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er in anderen Komponenten verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In klassischen ASP.NET-Core-Anwendungen entspricht die Lebensdauer eines solchen DbContext normalerweise einem HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für die vier grundlegenden Datenbankoperationen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + CRUD-Operationen                           |   |                   |   |        |&lt;br /&gt;
| -------------------------------------------- | - | ----------------- | - | ------ |&lt;br /&gt;
| ! Abkürzung !! Bedeutung !! SQL-Entsprechung |   |                   |   |        |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Create                                       |   | Datensatz anlegen |   | INSERT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Read                                         |   | Datensatz lesen   |   | SELECT |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Update                                       |   | Datensatz ändern  |   | UPDATE |&lt;br /&gt;
| -                                            |   |                   |   |        |&lt;br /&gt;
| Delete                                       |   | Datensatz löschen |   | DELETE |&lt;br /&gt;
| }                                            |   |                   |   |        |&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird erzeugt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird sie dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Änderung wird mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Komplettes Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Produkte anhand einer Bedingung filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, dass sich der Preis verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
Ein Datensatz kann folgendermaßen gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; – kurz &#039;&#039;&#039;LINQ&#039;&#039;&#039; – ist die wichtigste Abfragemethode für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine typische LINQ-Abfrage sieht beispielsweise folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diesen Ausdruck in eine SQL-Abfrage.&lt;br /&gt;
&lt;br /&gt;
=== Deferred Execution ==&lt;br /&gt;
&lt;br /&gt;
Viele LINQ-Abfragen werden zunächst nur definiert und noch nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu diesem Zeitpunkt wurde die Datenbank normalerweise noch nicht abgefragt.&lt;br /&gt;
&lt;br /&gt;
Die Abfrage wird beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await query.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
tatsächlich ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Dieses Verhalten wird als &#039;&#039;&#039;Deferred Execution&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige LINQ-Methoden ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete LINQ-Methoden mit EF Core |   |                                                  |&lt;br /&gt;
| --------------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Methode !! Funktion                         |   |                                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Where()                                       |   | Datensätze filtern                               |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| Select()                                      |   | Daten beziehungsweise Properties auswählen       |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderBy()                                     |   | aufsteigend sortieren                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| OrderByDescending()                           |   | absteigend sortieren                             |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ThenBy()                                      |   | zusätzliche Sortierung definieren                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstAsync()                                  |   | ersten Datensatz zurückgeben                     |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| FirstOrDefaultAsync()                         |   | ersten Datensatz oder Standardwert zurückgeben   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleAsync()                                 |   | genau einen Datensatz erwarten                   |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SingleOrDefaultAsync()                        |   | genau einen Datensatz oder Standardwert erwarten |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AnyAsync()                                    |   | prüfen, ob mindestens ein Datensatz existiert    |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| CountAsync()                                  |   | Datensätze zählen                                |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| SumAsync()                                    |   | Summe berechnen                                  |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| AverageAsync()                                |   | Durchschnitt berechnen                           |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MinAsync()                                    |   | Minimalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| MaxAsync()                                    |   | Maximalwert bestimmen                            |&lt;br /&gt;
| -                                             |   |                                                  |&lt;br /&gt;
| ToListAsync()                                 |   | Ergebnis als Liste laden                         |&lt;br /&gt;
| }                                             |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
== Projektionen ==&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Produktübersicht benötigt ausschließlich ID, Name und Preis.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann eine Projektion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Noch sauberer ist häufig die Verwendung eines DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductListDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch müssen nicht alle Spalten der Tabelle vom Datenbankserver übertragen werden.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei Web- und Serveranwendungen sollten Datenbankzugriffe nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beispiele synchroner und asynchroner EF-Core-Methoden |   |                       |&lt;br /&gt;
| ------------------------------------------------------- | - | --------------------- |&lt;br /&gt;
| ! Synchron !! Asynchron                                 |   |                       |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| ToList()                                                |   | ToListAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| First()                                                 |   | FirstAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| FirstOrDefault()                                        |   | FirstOrDefaultAsync() |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Single()                                                |   | SingleAsync()         |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| Count()                                                 |   | CountAsync()          |&lt;br /&gt;
| -                                                       |   |                       |&lt;br /&gt;
| SaveChanges()                                           |   | SaveChangesAsync()    |&lt;br /&gt;
| }                                                       |   |                       |&lt;br /&gt;
&lt;br /&gt;
Asynchrone Operationen sind insbesondere bei Serveranwendungen wichtig, da der ausführende Thread während einer Datenbankoperation nicht unnötig blockiert werden muss.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
EF Core überwacht standardmäßig Entities, die über einen DbContext geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt, dass sich die Property &amp;lt;code&amp;gt;Price&amp;lt;/code&amp;gt; verändert hat.&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird daraus beispielsweise ein entsprechender UPDATE-Befehl erzeugt.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
Eine vom Change Tracker verwaltete Entity kann unterschiedliche Zustände besitzen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zustände einer Entity im Change Tracker |   |                                                  |&lt;br /&gt;
| ----------------------------------------- | - | ------------------------------------------------ |&lt;br /&gt;
| ! Zustand !! Bedeutung                    |   |                                                  |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Detached                                  |   | Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Unchanged                                 |   | Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Added                                     |   | Entity soll neu eingefügt werden                 |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Modified                                  |   | Entity wurde verändert                           |&lt;br /&gt;
| -                                         |   |                                                  |&lt;br /&gt;
| Deleted                                   |   | Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                         |   |                                                  |&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand kann beispielsweise ermittelt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== AsNoTracking ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich gelesen werden und anschließend nicht verändert werden sollen, ist das Change Tracking häufig unnötig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann:&lt;br /&gt;
&lt;br /&gt;
* Speicher sparen&lt;br /&gt;
* den Verwaltungsaufwand reduzieren&lt;br /&gt;
* Read-Only-Abfragen beschleunigen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Entities nicht verändert und wieder gespeichert werden sollen, sollte geprüft werden, ob &amp;lt;code&amp;gt;AsNoTracking()&amp;lt;/code&amp;gt; sinnvoll ist.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration des Datenmodells ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet verschiedene Möglichkeiten zur Konfiguration des Datenmodells.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zur Konfiguration eines EF-Core-Modells |   |                                                                    |&lt;br /&gt;
| ------------------------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Verfahren !! Beschreibung                             |   |                                                                    |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Conventions                                             |   | automatische Erkennung anhand von Namens- und Strukturkonventionen |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Data Annotations                                        |   | Konfiguration über Attribute an C#-Klassen und Properties          |&lt;br /&gt;
| -                                                       |   |                                                                    |&lt;br /&gt;
| Fluent API                                              |   | Konfiguration über C#-Methoden im ModelBuilder                     |&lt;br /&gt;
| }                                                       |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Conventions ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht zunächst, die Struktur eines Datenmodells automatisch zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt dabei automatisch:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;Customer&amp;lt;/code&amp;gt; als Entity&lt;br /&gt;
* &amp;lt;code&amp;gt;Id&amp;lt;/code&amp;gt; als Primärschlüssel&lt;br /&gt;
* &amp;lt;code&amp;gt;Name&amp;lt;/code&amp;gt; als Datenbankfeld&lt;br /&gt;
&lt;br /&gt;
Diese automatische Konfiguration wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Eigenschaften einer Entity können über Attribute konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufig verwendete Data Annotations |   |                                                             |&lt;br /&gt;
| ------------------------------------ | - | ----------------------------------------------------------- |&lt;br /&gt;
| ! Attribut !! Bedeutung              |   |                                                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Key]                                |   | definiert einen Primärschlüssel                             |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Required]                           |   | markiert ein Pflichtfeld                                    |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [MaxLength]                          |   | definiert eine maximale Länge                               |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Column]                             |   | konfiguriert eine Datenbankspalte                           |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [Table]                              |   | konfiguriert den Tabellennamen                              |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [NotMapped]                          |   | Property wird nicht in der Datenbank gespeichert            |&lt;br /&gt;
| -                                    |   |                                                             |&lt;br /&gt;
| [ForeignKey]                         |   | definiert beziehungsweise kennzeichnet einen Fremdschlüssel |&lt;br /&gt;
| }                                    |   |                                                             |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Fluent API&#039;&#039;&#039; bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
Sie wird normalerweise innerhalb von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Anwendungen sollte die Konfiguration einzelner Entities häufig in separate Klassen ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationen können anschließend automatisch geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Variante sorgt bei umfangreichen Datenmodellen für eine deutlich bessere Übersicht.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
Relationale Datenbanken arbeiten intensiv mit Beziehungen zwischen Tabellen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt insbesondere:&lt;br /&gt;
&lt;br /&gt;
* 1:1-Beziehungen&lt;br /&gt;
* 1:n-Beziehungen&lt;br /&gt;
* n:m-Beziehungen&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Beziehungstypen       |   |                             |&lt;br /&gt;
| ----------------------- | - | --------------------------- |&lt;br /&gt;
| ! Beziehung !! Beispiel |   |                             |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:1                     |   | Benutzer und Benutzerprofil |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| 1:n                     |   | Kunde und Bestellungen      |&lt;br /&gt;
| -                       |   |                             |&lt;br /&gt;
| n:m                     |   | Produkte und Kategorien     |&lt;br /&gt;
| }                       |   |                             |&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung enthält den Fremdschlüssel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei stellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Fremdschlüssel dar.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public Customer Customer { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist eine &#039;&#039;&#039;Navigation Property&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Benutzer kann beispielsweise genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine explizite Konfiguration könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann mehreren Kategorien zugeordnet sein. Gleichzeitig kann eine Kategorie mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für eine einfache n:m-Beziehung automatisch eine Zwischentabelle verwalten.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst zusätzliche Eigenschaften besitzt, sollte dagegen eine eigene Entity verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   │&lt;br /&gt;
   │ 1:n&lt;br /&gt;
   ▼&lt;br /&gt;
ProductCategory&lt;br /&gt;
   ▲&lt;br /&gt;
   │ n:1&lt;br /&gt;
   │&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core bietet unterschiedliche Möglichkeiten, Navigation Properties zu laden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Strategien zum Laden von Beziehungen |   |                                                                    |&lt;br /&gt;
| -------------------------------------- | - | ------------------------------------------------------------------ |&lt;br /&gt;
| ! Strategie !! Beschreibung            |   |                                                                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Eager Loading                          |   | zusammengehörige Daten werden sofort mitgeladen                    |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Explicit Loading                       |   | zusammengehörige Daten werden gezielt nachgeladen                  |&lt;br /&gt;
| -                                      |   |                                                                    |&lt;br /&gt;
| Lazy Loading                           |   | Daten werden erst beim Zugriff auf die Navigation Property geladen |&lt;br /&gt;
| }                                      |   |                                                                    |&lt;br /&gt;
&lt;br /&gt;
== Eager Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Eager Loading&#039;&#039;&#039; werden Beziehungen direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Dies erfolgt mit &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit &amp;lt;code&amp;gt;ThenInclude()&amp;lt;/code&amp;gt; geladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Explicit Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Explicit Loading&#039;&#039;&#039; werden Beziehungen gezielt nachgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Lazy Loading ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Lazy Loading&#039;&#039;&#039; werden Navigation Properties automatisch geladen, sobald auf sie zugegriffen wird.&lt;br /&gt;
&lt;br /&gt;
Dies kann bequem sein, birgt jedoch die Gefahr einer großen Zahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere kann dadurch das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte deshalb bewusst und nicht automatisch als Standardlösung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jeden Kunden einzeln dessen Bestellliste geladen.&lt;br /&gt;
&lt;br /&gt;
Dann können beispielsweise folgende Datenbankzugriffe entstehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Lösung ist Eager Loading:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann eine gezielte Projektion verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen die kontrollierte Weiterentwicklung eines Datenbankschemas zusammen mit dem C#-Datenmodell.&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Entity &amp;lt;code&amp;gt;Product&amp;lt;/code&amp;gt; wird um eine Property ergänzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann eine Migration erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraus eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht kann diese beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Wichtige Befehle für EF-Core-Migrationen |   |                                                   |&lt;br /&gt;
| ------------------------------------------ | - | ------------------------------------------------- |&lt;br /&gt;
| ! Befehl !! Funktion                       |   |                                                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations add Name              |   | neue Migration erzeugen                           |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations list                  |   | vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations remove                |   | letzte Migration entfernen                        |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update                  |   | Datenbank auf die neueste Migration aktualisieren |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef database update MigrationName    |   | Datenbank auf eine bestimmte Migration setzen     |&lt;br /&gt;
| -                                          |   |                                                   |&lt;br /&gt;
| dotnet ef migrations script                |   | SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                          |   |                                                   |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, für geschäftskritische Systeme jedoch nicht zwangsläufig die beste Lösung.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess ermöglicht unter anderem:&lt;br /&gt;
&lt;br /&gt;
* Prüfung der Datenbankänderungen vor dem Deployment&lt;br /&gt;
* Erstellung und Archivierung von SQL-Skripten&lt;br /&gt;
* Durchführung eines Backups vor einer Migration&lt;br /&gt;
* Planung länger laufender Änderungen&lt;br /&gt;
* kontrollierte Vergabe von Datenbankberechtigungen&lt;br /&gt;
* einfachere Fehleranalyse&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Skript für vorhandene Migrationen kann beispielsweise erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# erstellt.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Modell wird anschließend das Datenbankschema erzeugt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Entity-Klassen&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
EF-Core-Modell&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Migration&lt;br /&gt;
        │&lt;br /&gt;
        ▼&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile von Code First:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode&lt;br /&gt;
* Datenmodell kann mit Git versioniert werden&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen&lt;br /&gt;
* gute Integration in CI/CD-Prozesse&lt;br /&gt;
* Datenbank und Anwendung können gemeinsam weiterentwickelt werden&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus C#-Klassen und einen DbContext erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Database First ist insbesondere bei bestehenden beziehungsweise älteren Datenbanken interessant.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann es bei der Modernisierung einer Legacy-Anwendung sinnvoll sein, eine bestehende Datenbank zunächst unverändert weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
== Code First und Database First im Vergleich ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich von Code First und Database First |   |                              |   |                             |&lt;br /&gt;
| --------------------------------------------- | - | ---------------------------- | - | --------------------------- |&lt;br /&gt;
| ! Eigenschaft !! Code First !! Database First |   |                              |   |                             |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Ausgangspunkt                                 |   | C#-Modell                    |   | vorhandene Datenbank        |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaerstellung                              |   | über EF Core und Migrationen |   | Datenbank existiert bereits |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für neue Anwendungen                 |   | sehr gut                     |   | eingeschränkt               |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Geeignet für Legacy-Datenbanken               |   | möglich                      |   | sehr gut                    |&lt;br /&gt;
| -                                             |   |                              |   |                             |&lt;br /&gt;
| Schemaänderungen                              |   | häufig über Migrationen      |   | häufig datenbankseitig      |&lt;br /&gt;
| }                                             |   |                              |   |                             |&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die vom Change Tracker registrierten Änderungen in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht ergibt sich folgende Zuordnung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zusammenhang zwischen Entity State und Datenbankoperation |   |                |&lt;br /&gt;
| ----------------------------------------------------------- | - | -------------- |&lt;br /&gt;
| ! Entity State !! Typische Datenbankoperation               |   |                |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Added                                                       |   | INSERT         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Modified                                                    |   | UPDATE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Deleted                                                     |   | DELETE         |&lt;br /&gt;
| -                                                           |   |                |&lt;br /&gt;
| Unchanged                                                   |   | keine Änderung |&lt;br /&gt;
| }                                                           |   |                |&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für einen einzelnen &amp;lt;code&amp;gt;SaveChanges()&amp;lt;/code&amp;gt;-Aufruf normalerweise eine Transaktion, sofern der verwendete Provider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für komplexere Abläufe können Transaktionen explizit verwaltet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
throw;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird sichergestellt, dass zusammengehörige Änderungen als Einheit behandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer oder Prozesse können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer A verändert und speichert Datensatz&lt;br /&gt;
        │&lt;br /&gt;
Benutzer B verändert und speichert Datensatz&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Kontrolle könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Bei SQL Server kann beispielsweise eine RowVersion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischen Lesen und Schreiben durch einen anderen Prozess verändert, kann EF Core eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auslösen.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie dieser Konflikt behandelt werden soll.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei Massenänderungen ist es häufig unnötig, alle betroffenen Entities zunächst in den Arbeitsspeicher zu laden.&lt;br /&gt;
&lt;br /&gt;
Eine klassische Vorgehensweise wäre:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann &amp;lt;code&amp;gt;ExecuteUpdateAsync()&amp;lt;/code&amp;gt; verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Datensätze können direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Verfahren vermeiden das vorherige Laden sämtlicher betroffener Entities.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core ermöglicht zusätzlich die Ausführung von SQL.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei SQL-Abfragen müssen immer parameterisierte Verfahren verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann zu &#039;&#039;&#039;SQL-Injection-Schwachstellen&#039;&#039;&#039; führen.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Mit einem &#039;&#039;&#039;Global Query Filter&#039;&#039;&#039; kann eine Bedingung definiert werden, die automatisch auf Abfragen einer Entity angewendet wird.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur nicht gelöschte Produkte.&lt;br /&gt;
&lt;br /&gt;
Der Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einsatzmöglichkeiten globaler Filter sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Multi-Tenant-Anwendungen&lt;br /&gt;
* Mandantenfilter&lt;br /&gt;
* Archivierung&lt;br /&gt;
* zeitabhängige Datensichten&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Datenbankindizes können über EF Core konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Leseoperationen erheblich beschleunigen.&lt;br /&gt;
&lt;br /&gt;
Sie haben jedoch auch Nachteile:&lt;br /&gt;
&lt;br /&gt;
* zusätzlicher Speicherverbrauch&lt;br /&gt;
* zusätzlicher Aufwand bei INSERT&lt;br /&gt;
* zusätzlicher Aufwand bei UPDATE&lt;br /&gt;
* zusätzlicher Aufwand bei DELETE&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes gehört deshalb weiterhin zum Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Value Conversions&#039;&#039;&#039; können .NET-Werte beim Speichern in einen anderen Datenbankwert umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Ein Enum kann beispielsweise als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
könnten dann beispielsweise folgende Werte gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Complex Types ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt komplexe Typen zur Abbildung strukturierter Werte.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Geeignete Einsatzbereiche sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Geldwerte&lt;br /&gt;
* wiederverwendbare Value Objects&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten normalerweise nicht vollständig auf einmal geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ein einfaches Paging kann mit &amp;lt;code&amp;gt;Skip()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;Take()&amp;lt;/code&amp;gt; umgesetzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit werden 50 Datensätze ab Position 101 geladen.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenbestände kann sogenanntes &#039;&#039;&#039;Keyset Paging&#039;&#039;&#039; beziehungsweise &#039;&#039;&#039;Cursor Paging&#039;&#039;&#039; effizienter sein.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen mit mehreren Includes kann eine einzelne SQL-Abfrage sehr große Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Split Queries&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei erzeugt EF Core mehrere SQL-Abfragen.&lt;br /&gt;
&lt;br /&gt;
Ob Single Query oder Split Query effizienter ist, hängt vom jeweiligen Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== Erzeugtes SQL anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Bei der Analyse komplexer Abfragen ist es häufig sinnvoll, das von EF Core erzeugte SQL zu betrachten.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage kann beispielsweise zunächst gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das zugehörige SQL kann anschließend ausgegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql = query.ToQueryString();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist besonders hilfreich für:&lt;br /&gt;
&lt;br /&gt;
* Performanceanalysen&lt;br /&gt;
* Fehlersuche&lt;br /&gt;
* Prüfung von JOINs&lt;br /&gt;
* Prüfung von Filtern&lt;br /&gt;
* Analyse komplexer LINQ-Abfragen&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core integriert sich in das Logging-System von .NET.&lt;br /&gt;
&lt;br /&gt;
Eine einfache Konsolenausgabe kann beispielsweise aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit können unter anderem folgende Informationen sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* ausgeführte SQL-Befehle&lt;br /&gt;
* Dauer von Datenbankoperationen&lt;br /&gt;
* EF-Core-Warnungen&lt;br /&gt;
* Fehler&lt;br /&gt;
* Verbindungsinformationen&lt;br /&gt;
* Transaktionen&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in Produktivsystemen nicht unkontrolliert protokolliert werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
Die Abstraktion von EF Core erleichtert die Entwicklung, kann jedoch dazu führen, dass teure Datenbankoperationen im C#-Code zunächst unscheinbar wirken.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Punkte untersucht werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Datenbank-Roundtrips&lt;br /&gt;
* Paging&lt;br /&gt;
* Projektionen&lt;br /&gt;
* Datenbankausführungspläne&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Wenn lediglich die Namen von Produkten benötigt werden, sollte nicht die vollständige Entity geladen werden.&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var names = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Datenmenge und Speicherbedarf reduziert werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als &#039;&#039;&#039;kurzlebige Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Ablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Daten laden&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
SaveChanges&lt;br /&gt;
       │&lt;br /&gt;
       ▼&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton verwendet werden&lt;br /&gt;
* über sehr lange Zeiträume existieren&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In einigen Anwendungstypen ist es sinnvoll, DbContext-Instanzen über eine Factory zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dies gilt beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Background Services&lt;br /&gt;
* Worker Services&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer eines DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein klassischer ASP.NET-Core-Request existiert nur relativ kurz.&lt;br /&gt;
&lt;br /&gt;
Ein Blazor-Circuit kann dagegen deutlich länger bestehen.&lt;br /&gt;
&lt;br /&gt;
Daher kann die Verwendung von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann für einen Vorgang eine eigene DbContext-Instanz verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme durch:&lt;br /&gt;
&lt;br /&gt;
* lange Tracking-Zeiträume&lt;br /&gt;
* parallele Datenbankoperationen&lt;br /&gt;
* veraltete Entity-Zustände&lt;br /&gt;
* unnötigen Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Eine Schnittstelle könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften des Repository- und Unit-of-Work-Patterns.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Zuordnung von EF-Core-Komponenten zu bekannten Patterns |   |              |&lt;br /&gt;
| --------------------------------------------------------- | - | ------------ |&lt;br /&gt;
| ! EF-Core-Komponente !! Ähnliches Entwurfsmuster          |   |              |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbSet                                                     |   | Repository   |&lt;br /&gt;
| -                                                         |   |              |&lt;br /&gt;
| DbContext                                                 |   | Unit of Work |&lt;br /&gt;
| }                                                         |   |              |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein Repository, das lediglich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
um ein DbSet herum implementiert, erzeugt möglicherweise nur eine zusätzliche Abstraktionsschicht ohne echten Mehrwert.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch gleichzeitig als API-Datenmodelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Häufig ist eine Trennung sinnvoll zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird verhindert, dass interne Datenbankstrukturen automatisch Bestandteil einer öffentlichen Schnittstelle werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core parametrisiert normale LINQ-Abfragen automatisch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet für &amp;lt;code&amp;gt;userInput&amp;lt;/code&amp;gt; normalerweise einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Dies reduziert das Risiko von SQL Injection erheblich.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsmaßnahmen beachtet werden:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Zugangsdaten über Secret Stores oder sichere Konfiguration verwalten&lt;br /&gt;
* Least-Privilege-Prinzip für Datenbankkonten verwenden&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert ausführen&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Clients ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
* produktive Zugangsdaten nicht in Git speichern&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf verschiedene Arten getestet werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Möglichkeiten zum Testen von EF-Core-Anwendungen |   |                                                                        |&lt;br /&gt;
| -------------------------------------------------- | - | ---------------------------------------------------------------------- |&lt;br /&gt;
| ! Verfahren !! Eigenschaften                       |   |                                                                        |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| echte Testdatenbank                                |   | sehr realitätsnah                                                      |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| temporäre Datenbank                                |   | gut für automatisierte Integrationstests                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| SQLite In-Memory                                   |   | relationale Datenbank im Arbeitsspeicher                               |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| EF-Core-InMemory-Provider                          |   | schnell, aber kein vollständiger Ersatz für eine relationale Datenbank |&lt;br /&gt;
| -                                                  |   |                                                                        |&lt;br /&gt;
| Testcontainer                                      |   | echtes Datenbanksystem innerhalb eines Containers                      |&lt;br /&gt;
| }                                                  |   |                                                                        |&lt;br /&gt;
&lt;br /&gt;
== InMemory Provider ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen eigenen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für einfache Tests hilfreich sein.&lt;br /&gt;
&lt;br /&gt;
Er verhält sich allerdings nicht vollständig wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Unterschiede bestehen unter anderem bei:&lt;br /&gt;
&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
* relationalen Constraints&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* providerabhängigem Verhalten&lt;br /&gt;
&lt;br /&gt;
Ein Test, der mit dem InMemory-Provider erfolgreich ist, muss deshalb nicht zwangsläufig mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
== SQLite In-Memory ==&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher eine bessere Annäherung sein.&lt;br /&gt;
&lt;br /&gt;
Auch SQLite verhält sich jedoch nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
Wenn providerabhängiges Verhalten getestet werden soll, ist eine Testinstanz des tatsächlich eingesetzten Datenbanksystems zuverlässiger.&lt;br /&gt;
&lt;br /&gt;
== Integrationstests mit Containern ==&lt;br /&gt;
&lt;br /&gt;
Für komplexe Anwendungen können Datenbanken während eines Tests temporär als Container gestartet werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Integrationstest&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
Docker / Testcontainer&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
temporärer SQL Server&lt;br /&gt;
oder PostgreSQL&lt;br /&gt;
      │&lt;br /&gt;
      ▼&lt;br /&gt;
EF Core&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch können Tests gegen ein echtes Datenbanksystem ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen oder erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Einsatzmöglichkeiten sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* automatische Ergänzung bestimmter Werte&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser kann vor oder nach einem SaveChanges-Vorgang eigenen Code ausführen.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Geschäftsanwendungen müssen speichern, wann ein Datensatz erzeugt oder verändert wurde.&lt;br /&gt;
&lt;br /&gt;
Eine gemeinsame Basisklasse könnte beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere mögliche Auditfelder sind:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
* DeletedAt&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise innerhalb von SaveChanges oder über einen Interceptor automatisch gepflegt werden.&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Soft Delete&#039;&#039;&#039; wird ein Datensatz nicht physisch aus der Datenbank gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen kann beispielsweise eine Property verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Löschen wird dann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
product.IsDeleted = true;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vor- und Nachteile von Soft Delete  |   |                                                        |&lt;br /&gt;
| ------------------------------------- | - | ------------------------------------------------------ |&lt;br /&gt;
| ! Vorteile !! Nachteile               |   |                                                        |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Daten können wiederhergestellt werden |   | Datenbank wächst dauerhaft weiter                      |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Historie bleibt erhalten              |   | Abfragen müssen gelöschte Datensätze berücksichtigen   |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Beziehungen bleiben nachvollziehbar   |   | Unique Constraints können komplizierter werden         |&lt;br /&gt;
| -                                     |   |                                                        |&lt;br /&gt;
| Auditing wird erleichtert             |   | Datenschutz kann trotzdem physische Löschung erfordern |&lt;br /&gt;
| }                                     |   |                                                        |&lt;br /&gt;
&lt;br /&gt;
Soft Delete wird häufig mit Global Query Filters kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
=== Komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products =&lt;br /&gt;
    (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst sämtliche Produkte übertragen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Daten ausschließlich angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Eine große Zahl von &amp;lt;code&amp;gt;Include()&amp;lt;/code&amp;gt;-Aufrufen kann zu sehr großen SQL-Abfragen und Ergebnismengen führen.&lt;br /&gt;
&lt;br /&gt;
Eine gezielte Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Datenbankabfragen können erheblich langsamer sein als eine oder wenige gezielt formulierte Abfragen.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine gut formulierte EF-Core-Abfrage kann langsam sein, wenn wichtige Datenbankindizes fehlen.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler und Probleme ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Häufige EF-Core-Probleme                     |   |                                                                             |&lt;br /&gt;
| ---------------------------------------------- | - | --------------------------------------------------------------------------- |&lt;br /&gt;
| ! Problem !! Typische Ursache                  |   |                                                                             |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| DbContext wurde bereits disposed               |   | DbContext wird außerhalb seiner vorgesehenen Lebensdauer verwendet          |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| A second operation was started on this context |   | mehrere parallele Operationen verwenden denselben DbContext                 |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Entity wird bereits getrackt                   |   | mehrere Instanzen mit gleichem Primärschlüssel werden gleichzeitig verfolgt |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| LINQ-Ausdruck kann nicht übersetzt werden      |   | verwendeter C#-Ausdruck besitzt keine SQL-Übersetzung                       |&lt;br /&gt;
| -                                              |   |                                                                             |&lt;br /&gt;
| Migration passt nicht zur Datenbank            |   | Schema und Migrationshistorie sind nicht synchron                           |&lt;br /&gt;
| }                                              |   |                                                                             |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Bei ADO.NET müssen Entwickler häufig selbst:&lt;br /&gt;
&lt;br /&gt;
* SQL schreiben&lt;br /&gt;
* Connection verwalten&lt;br /&gt;
* Commands erstellen&lt;br /&gt;
* Parameter definieren&lt;br /&gt;
* DataReader verarbeiten&lt;br /&gt;
* Datensätze auf Objekte abbilden&lt;br /&gt;
&lt;br /&gt;
EF Core übernimmt einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und ADO.NET |   |                    |   |                               |&lt;br /&gt;
| ------------------------------------------------------ | - | ------------------ | - | ----------------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! ADO.NET                    |   |                    |   |                               |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abstraktionsgrad                                       |   | hoch               |   | niedrig                       |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Abfragesprache                                         |   | hauptsächlich LINQ |   | hauptsächlich SQL             |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Objekt-Mapping                                         |   | automatisch        |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Change Tracking                                        |   | integriert         |   | normalerweise manuell         |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Migrationen                                            |   | integriert         |   | nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Kontrolle über SQL                                     |   | geringer           |   | sehr hoch                     |&lt;br /&gt;
| -                                                      |   |                    |   |                               |&lt;br /&gt;
| Entwicklungsaufwand                                    |   | häufig geringer    |   | häufig höher                  |&lt;br /&gt;
| }                                                      |   |                    |   |                               |&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter &#039;&#039;&#039;Micro-ORM&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dapper konzentriert sich stärker auf das Mapping von SQL-Ergebnissen auf .NET-Objekte.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Vergleich zwischen Entity Framework Core und Dapper |   |                            |   |                        |&lt;br /&gt;
| ----------------------------------------------------- | - | -------------------------- | - | ---------------------- |&lt;br /&gt;
| ! Eigenschaft !! EF Core !! Dapper                    |   |                            |   |                        |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| LINQ-Abfragen                                         |   | Ja                         |   | normalerweise Nein     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| automatische SQL-Erzeugung                            |   | Ja                         |   | überwiegend Nein       |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Change Tracking                                       |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Migrationen                                           |   | Ja                         |   | Nein                   |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Beziehungen                                           |   | umfangreiche Unterstützung |   | weitgehend manuell     |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Abstraktionsgrad                                      |   | hoch                       |   | niedrig                |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| Kontrolle über SQL                                    |   | mittel bis hoch            |   | sehr hoch              |&lt;br /&gt;
| -                                                     |   |                            |   |                        |&lt;br /&gt;
| CRUD-Entwicklung                                      |   | sehr komfortabel           |   | stärker SQL-orientiert |&lt;br /&gt;
| }                                                     |   |                            |   |                        |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Ein mögliches Szenario wäre:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für einzelne hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt kein Datenbankwissen ==&lt;br /&gt;
&lt;br /&gt;
Auch bei der Verwendung eines ORM bleiben Kenntnisse relationaler Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Entwickler sollten insbesondere folgende Konzepte verstehen:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Joins&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* SQL-Datentypen&lt;br /&gt;
* Datenbankperformance&lt;br /&gt;
&lt;br /&gt;
EF Core kann ein schlechtes Datenmodell nicht automatisch in ein gutes Datenmodell umwandeln.&lt;br /&gt;
&lt;br /&gt;
== EF Core ersetzt keine SQL-Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
Für einfache Anwendungen ist es möglich, einen großen Teil der Datenbankzugriffe ausschließlich mit LINQ umzusetzen.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse dennoch wichtig.&lt;br /&gt;
&lt;br /&gt;
Sie helfen beispielsweise bei:&lt;br /&gt;
&lt;br /&gt;
* Analyse des erzeugten SQL&lt;br /&gt;
* Performanceoptimierung&lt;br /&gt;
* Planung von Indizes&lt;br /&gt;
* Analyse von JOINs&lt;br /&gt;
* Verständnis von Ausführungsplänen&lt;br /&gt;
* Entwicklung komplexer Reports&lt;br /&gt;
* Fehleranalyse&lt;br /&gt;
* Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist deshalb besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Vorteilen gehören:&lt;br /&gt;
&lt;br /&gt;
* hoher Abstraktionsgrad&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Abbildung zwischen Objekten und Datenbank&lt;br /&gt;
* integriertes Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung verschiedener Datenbanksysteme&lt;br /&gt;
* Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* integrierte Behandlung von Beziehungen&lt;br /&gt;
* gute Unterstützung moderner .NET-Anwendungen&lt;br /&gt;
* automatische Parameterisierung vieler Datenbankabfragen&lt;br /&gt;
* hohe Entwicklungsgeschwindigkeit&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch einige Nachteile beziehungsweise Risiken:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* erzeugtes SQL ist nicht unmittelbar sichtbar&lt;br /&gt;
* ungeschickte LINQ-Abfragen können ineffizientes SQL erzeugen&lt;br /&gt;
* komplexe Abfragen können schwer nachvollziehbar sein&lt;br /&gt;
* Change Tracking benötigt zusätzliche Ressourcen&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Spezialabfragen können Raw SQL erfordern&lt;br /&gt;
* Entwickler können Performanceprobleme verursachen, ohne dies im C#-Code sofort zu erkennen&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine klar begrenzte Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Asynchrone APIs verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Web- und Serveranwendungen sollten normalerweise Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nur gelesen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Nur benötigte Daten auswählen ===&lt;br /&gt;
&lt;br /&gt;
DTO-Projektionen reduzieren häufig Datenmenge und Speicherbedarf.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien sollten normalerweise zusammen mit dem Quellcode in Git gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen vor dem Deployment prüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor einer produktiven Migration sollte geprüft werden, welche Änderungen tatsächlich auf der Datenbank ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können Abfragen mit einem CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generiertes SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Komplexe LINQ-Abfragen sollten bei Bedarf mit &amp;lt;code&amp;gt;ToQueryString()&amp;lt;/code&amp;gt; analysiert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt keine Datenbankoptimierung.&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Sortier- und Join-Spalten sollten hinsichtlich sinnvoller Indizes überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== DbContext nicht parallel verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine einzelne DbContext-Instanz darf nicht für mehrere parallele Datenbankoperationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik vom DbContext trennen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte sich primär um Datenzugriff, Mapping und Persistenz kümmern.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftsregeln gehören normalerweise in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer einfachen EF-Core-Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt anlegen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkte lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Produkt löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products&lt;br /&gt;
    .FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mögliche Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Für eine größere Anwendung könnte beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MyApplication&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── DTOs&lt;br /&gt;
│   ├── Interfaces&lt;br /&gt;
│   └── Services&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Components&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei kleineren Projekten ist eine derart umfangreiche Trennung nicht zwingend erforderlich.&lt;br /&gt;
&lt;br /&gt;
Die Architektur sollte sich an der tatsächlichen Komplexität der Anwendung orientieren.&lt;br /&gt;
&lt;br /&gt;
== Aktuelle EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
Stand September 2026 ist &#039;&#039;&#039;EF Core 10&#039;&#039;&#039; die aktuelle stabile LTS-Version.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;margin:auto&amp;quot;&lt;br /&gt;
&lt;br /&gt;
| + Aktuell relevante EF-Core-Versionen         |   |         |   |                       |&lt;br /&gt;
| --------------------------------------------- | - | ------- | - | --------------------- |&lt;br /&gt;
| ! EF-Core-Version !! Zielframework !! Support |   |         |   |                       |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 10                                    |   | .NET 10 |   | bis 10. November 2028 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 9                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| -                                             |   |         |   |                       |&lt;br /&gt;
| EF Core 8                                     |   | .NET 8  |   | bis 10. November 2026 |&lt;br /&gt;
| }                                             |   |         |   |                       |&lt;br /&gt;
&lt;br /&gt;
EF Core 10 benötigt das .NET-10-SDK zum Kompilieren und die .NET-10-Runtime zur Ausführung.&lt;br /&gt;
&lt;br /&gt;
Bei einem Upgrade zwischen Hauptversionen sollten grundsätzlich die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; überprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die verwendeten EF-Core-Pakete sollten außerdem möglichst dieselbe Hauptversion verwenden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore             10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer   10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design      10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Drittanbieter-Providern muss zusätzlich geprüft werden, ob die jeweilige Version die verwendete EF-Core-Hauptversion unterstützt.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit vielen Beziehungen zwischen Entities&lt;br /&gt;
* Anwendungen mit Code-First-Datenmodellen&lt;br /&gt;
* Systeme mit versionierten Datenbankmigrationen&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Datenzugriffstechnologien können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* nur sehr wenige und einfache SQL-Abfragen benötigt werden&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement erforderlich ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine ungewöhnliche Legacy-Datenbank verwendet wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* keine objektorientierte Abbildung der Daten erforderlich ist&lt;br /&gt;
&lt;br /&gt;
Es ist ebenfalls möglich, EF Core mit anderen Technologien wie ADO.NET oder Dapper zu kombinieren.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist das zentrale ORM-Framework für moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbSet&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
DbContext&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Database Provider&lt;br /&gt;
   │&lt;br /&gt;
   ▼&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entities&#039;&#039;&#039; repräsentieren Datensätze.&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; repräsentiert eine Menge von Entities.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; dient zur Formulierung von Abfragen.&lt;br /&gt;
* &#039;&#039;&#039;Change Tracking&#039;&#039;&#039; erkennt Veränderungen an Entities.&lt;br /&gt;
* &#039;&#039;&#039;SaveChanges&#039;&#039;&#039; überträgt Änderungen in die Datenbank.&lt;br /&gt;
* &#039;&#039;&#039;Migrationen&#039;&#039;&#039; verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* &#039;&#039;&#039;Navigation Properties&#039;&#039;&#039; bilden Beziehungen zwischen Entities ab.&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* &#039;&#039;&#039;AsNoTracking&#039;&#039;&#039; kann Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* &#039;&#039;&#039;Projektionen&#039;&#039;&#039; reduzieren unnötige Datenübertragungen.&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; sollte kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet für viele Geschäftsanwendungen einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039;, &#039;&#039;&#039;Abstraktion&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
* [[NuGet]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn – Entity Framework Core&lt;br /&gt;
* Microsoft Learn – EF Core Releases and Planning&lt;br /&gt;
* Microsoft Learn – EF Core Database Providers&lt;br /&gt;
* Microsoft Learn – EF Core Change Tracking&lt;br /&gt;
* Microsoft Learn – EF Core Relationships&lt;br /&gt;
* Microsoft Learn – EF Core Migrations&lt;br /&gt;
* Microsoft Learn – EF Core Performance&lt;br /&gt;
* Microsoft Learn – What&#039;s New in EF Core 10&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=210</id>
		<title>Entity Framework Core (EF Core)</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entity_Framework_Core_(EF_Core)&amp;diff=210"/>
		<updated>2026-09-09T08:43:48Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „{{DISPLAYTITLE:Entity Framework Core}} **TOC**  &amp;#039;&amp;#039;&amp;#039;Entity Framework Core&amp;#039;&amp;#039;&amp;#039; (kurz &amp;#039;&amp;#039;&amp;#039;EF Core&amp;#039;&amp;#039;&amp;#039;) ist ein modernes &amp;#039;&amp;#039;&amp;#039;Object-Relational Mapping Framework&amp;#039;&amp;#039;&amp;#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.  EF Core bildet Datenbanktabellen auf .NET-Klassen und Datensätze auf Objekte dieser Klassen ab. Abf…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{DISPLAYTITLE:Entity Framework Core}}&lt;br /&gt;
**TOC**&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; (kurz &#039;&#039;&#039;EF Core&#039;&#039;&#039;) ist ein modernes &#039;&#039;&#039;Object-Relational Mapping Framework&#039;&#039;&#039; (ORM) für .NET. Es ermöglicht .NET-Anwendungen den Zugriff auf relationale und teilweise auch nichtrelationale Datenbanken, ohne dass für jede Datenbankoperation manuell SQL geschrieben werden muss.&lt;br /&gt;
&lt;br /&gt;
EF Core bildet Datenbanktabellen auf .NET-Klassen und Datensätze auf Objekte dieser Klassen ab. Abfragen können in der Regel mit &#039;&#039;&#039;LINQ&#039;&#039;&#039; formuliert werden. EF Core übersetzt diese LINQ-Abfragen anschließend in die Abfragesprache des jeweiligen Datenbanksystems, beispielsweise SQL.&lt;br /&gt;
&lt;br /&gt;
EF Core ist der moderne Nachfolger des klassischen &#039;&#039;&#039;Entity Framework&#039;&#039;&#039;. Es wurde für .NET Core beziehungsweise das heutige plattformübergreifende .NET neu entwickelt.&lt;br /&gt;
&lt;br /&gt;
Die aktuelle LTS-Generation ist &#039;&#039;&#039;Entity Framework Core 10&#039;&#039;&#039;, die zusammen mit &#039;&#039;&#039;.NET 10&#039;&#039;&#039; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
== Grundprinzip ==&lt;br /&gt;
&lt;br /&gt;
Ohne ORM könnte eine Anwendung beispielsweise eine SQL-Abfrage direkt an eine Datenbank senden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
SELECT Id, Name, Price&lt;br /&gt;
FROM Products&lt;br /&gt;
WHERE Price &amp;gt; 100;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend müssten die zurückgegebenen Datensätze manuell in C#-Objekte umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Mit Entity Framework Core kann dieselbe Abfrage beispielsweise folgendermaßen formuliert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core übersetzt diese LINQ-Abfrage in eine für den verwendeten Datenbankprovider geeignete Datenbankabfrage.&lt;br /&gt;
&lt;br /&gt;
Das grundlegende Prinzip lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C#-Objekte&lt;br /&gt;
    ↓&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
    ↓&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
    ↓&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Lesen funktioniert die Verarbeitung entsprechend in die andere Richtung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Datenbank&lt;br /&gt;
    ↓&lt;br /&gt;
Datenbankprovider&lt;br /&gt;
    ↓&lt;br /&gt;
Entity Framework Core&lt;br /&gt;
    ↓&lt;br /&gt;
C#-Objekte&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ORM – Object-Relational Mapping ==&lt;br /&gt;
&lt;br /&gt;
EF Core gehört zur Kategorie der &#039;&#039;&#039;Object-Relational Mapper&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dabei müssen zwei unterschiedliche Datenmodelle miteinander verbunden werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Objektorientierte Anwendung&lt;br /&gt;
&lt;br /&gt;
| ! Relationale Datenbank |&lt;br /&gt;
| ----------------------- |&lt;br /&gt;
| Klasse                  |&lt;br /&gt;
| Tabelle                 |&lt;br /&gt;
| -                       |&lt;br /&gt;
| Objekt                  |&lt;br /&gt;
| Datensatz / Zeile       |&lt;br /&gt;
| -                       |&lt;br /&gt;
| Property                |&lt;br /&gt;
| Spalte                  |&lt;br /&gt;
| -                       |&lt;br /&gt;
| Objekt-ID               |&lt;br /&gt;
| Primärschlüssel         |&lt;br /&gt;
| -                       |&lt;br /&gt;
| Referenz auf Objekt     |&lt;br /&gt;
| Fremdschlüssel          |&lt;br /&gt;
| -                       |&lt;br /&gt;
| Collection              |&lt;br /&gt;
| 1:n-Beziehung           |&lt;br /&gt;
| }                       |&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann die Klasse:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
von EF Core auf eine Datenbanktabelle wie diese abgebildet werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Id&lt;br /&gt;
! Name&lt;br /&gt;
&lt;br /&gt;
| ! Price           |&lt;br /&gt;
| ----------------- |&lt;br /&gt;
| 1                 |&lt;br /&gt;
| Kalkammonsalpeter |&lt;br /&gt;
| 289.00            |&lt;br /&gt;
| -                 |&lt;br /&gt;
| 2                 |&lt;br /&gt;
| Harnstoff         |&lt;br /&gt;
| 315.00            |&lt;br /&gt;
| -                 |&lt;br /&gt;
| 3                 |&lt;br /&gt;
| Kali 60           |&lt;br /&gt;
| 245.00            |&lt;br /&gt;
| }                 |&lt;br /&gt;
&lt;br /&gt;
== Wichtige Komponenten ==&lt;br /&gt;
&lt;br /&gt;
Zu den wichtigsten Komponenten von EF Core gehören:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Entity&#039;&#039;&#039; – eine C#-Klasse, die einen Datensatz repräsentiert&lt;br /&gt;
* &#039;&#039;&#039;DbContext&#039;&#039;&#039; – zentrale Schnittstelle zwischen Anwendung und Datenbank&lt;br /&gt;
* &#039;&#039;&#039;DbSet&#039;&#039;&#039; – repräsentiert eine Menge von Entities eines bestimmten Typs&lt;br /&gt;
* &#039;&#039;&#039;LINQ&#039;&#039;&#039; – dient zur Formulierung von Datenbankabfragen&lt;br /&gt;
* &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; – überwacht Veränderungen an geladenen Entities&lt;br /&gt;
* &#039;&#039;&#039;Database Provider&#039;&#039;&#039; – setzt EF-Core-Operationen für ein konkretes Datenbanksystem um&lt;br /&gt;
* &#039;&#039;&#039;Migrations&#039;&#039;&#039; – verwalten Änderungen am Datenbankschema&lt;br /&gt;
* &#039;&#039;&#039;Model Configuration&#039;&#039;&#039; – definiert die Abbildung zwischen C#-Modell und Datenbank&lt;br /&gt;
&lt;br /&gt;
== DbContext ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;DbContext&#039;&#039;&#039; ist die zentrale Komponente von Entity Framework Core.&lt;br /&gt;
&lt;br /&gt;
Er stellt eine Sitzung mit der Datenbank dar und ist unter anderem verantwortlich für:&lt;br /&gt;
&lt;br /&gt;
* Datenbankabfragen&lt;br /&gt;
* Erzeugen neuer Datensätze&lt;br /&gt;
* Ändern bestehender Datensätze&lt;br /&gt;
* Löschen von Datensätzen&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Konfiguration des Datenmodells&lt;br /&gt;
&lt;br /&gt;
Ein einfacher DbContext kann folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
public ApplicationDbContext(&lt;br /&gt;
DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
: base(options)&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der DbContext enthält in diesem Beispiel eine Menge von Produkten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese wird typischerweise auf eine Tabelle der Datenbank abgebildet.&lt;br /&gt;
&lt;br /&gt;
== Entity ==&lt;br /&gt;
&lt;br /&gt;
Eine Entity ist normalerweise eine einfache C#-Klasse.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt viele Eigenschaften automatisch anhand von Konventionen.&lt;br /&gt;
&lt;br /&gt;
Die Property:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird beispielsweise standardmäßig als Primärschlüssel interpretiert.&lt;br /&gt;
&lt;br /&gt;
Alternativ würde auch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public int ProductId { get; set; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Primärschlüssel erkannt.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird normalerweise über &#039;&#039;&#039;NuGet&#039;&#039;&#039; installiert.&lt;br /&gt;
&lt;br /&gt;
=== Basispaket ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird ein Provider für die gewünschte Datenbank benötigt.&lt;br /&gt;
&lt;br /&gt;
=== SQL Server ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== SQLite ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Design-Paket ===&lt;br /&gt;
&lt;br /&gt;
Für Migrationen und andere Entwicklungswerkzeuge:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet add package Microsoft.EntityFrameworkCore.Design&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== EF-Core-Kommandozeilentool ===&lt;br /&gt;
&lt;br /&gt;
Das Werkzeug &#039;&#039;&#039;dotnet-ef&#039;&#039;&#039; kann global installiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool install --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine vorhandene Installation kann aktualisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet tool update --global dotnet-ef&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Datenbankprovider ==&lt;br /&gt;
&lt;br /&gt;
EF Core trennt die allgemeine ORM-Funktionalität vom konkreten Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Für jedes Datenbanksystem wird deshalb ein entsprechender &#039;&#039;&#039;Provider&#039;&#039;&#039; benötigt.&lt;br /&gt;
&lt;br /&gt;
Typische Provider sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Datenbank&lt;br /&gt;
&lt;br /&gt;
| ! Provider                              |&lt;br /&gt;
| --------------------------------------- |&lt;br /&gt;
| Microsoft SQL Server                    |&lt;br /&gt;
| Microsoft.EntityFrameworkCore.SqlServer |&lt;br /&gt;
| -                                       |&lt;br /&gt;
| SQLite                                  |&lt;br /&gt;
| Microsoft.EntityFrameworkCore.Sqlite    |&lt;br /&gt;
| -                                       |&lt;br /&gt;
| PostgreSQL                              |&lt;br /&gt;
| Npgsql.EntityFrameworkCore.PostgreSQL   |&lt;br /&gt;
| -                                       |&lt;br /&gt;
| MySQL / MariaDB                         |&lt;br /&gt;
| z. B. Pomelo.EntityFrameworkCore.MySql  |&lt;br /&gt;
| -                                       |&lt;br /&gt;
| Oracle Database                         |&lt;br /&gt;
| entsprechender Oracle-EF-Core-Provider  |&lt;br /&gt;
| -                                       |&lt;br /&gt;
| Azure Cosmos DB                         |&lt;br /&gt;
| Microsoft.EntityFrameworkCore.Cosmos    |&lt;br /&gt;
| }                                       |&lt;br /&gt;
&lt;br /&gt;
Nicht jeder Provider unterstützt sämtliche EF-Core-Funktionen identisch. Insbesondere spezielle SQL-Funktionen, Datentypen oder Datenbankfeatures können providerabhängig sein.&lt;br /&gt;
&lt;br /&gt;
== Verbindung zur Datenbank ==&lt;br /&gt;
&lt;br /&gt;
Die Datenbankverbindung wird über einen &#039;&#039;&#039;Connection String&#039;&#039;&#039; konfiguriert.&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Server-Connection-String könnte beispielsweise so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Server=localhost;Database=TestDb;User Id=testuser;Password=Passwort;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In ASP.NET Core wird der Connection String üblicherweise in einer Konfigurationsdatei gespeichert.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;json&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;ConnectionStrings&amp;quot;: {&lt;br /&gt;
    &amp;quot;DefaultConnection&amp;quot;: &amp;quot;Server=localhost;Database=TestDb;Trusted_Connection=True;TrustServerCertificate=True&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der DbContext kann anschließend registriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dependency Injection ==&lt;br /&gt;
&lt;br /&gt;
In ASP.NET Core wird der DbContext normalerweise über das integrierte &#039;&#039;&#039;Dependency-Injection-System&#039;&#039;&#039; registriert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(options =&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    options.UseSqlServer(&lt;br /&gt;
        builder.Configuration.GetConnectionString(&amp;quot;DefaultConnection&amp;quot;));&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann er beispielsweise in einen Service injiziert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ASP.NET Core verwaltet dabei standardmäßig die Lebensdauer des DbContext passend zum jeweiligen Request.&lt;br /&gt;
&lt;br /&gt;
== CRUD-Operationen ==&lt;br /&gt;
&lt;br /&gt;
CRUD steht für:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Create&#039;&#039;&#039; – Datensatz erzeugen&lt;br /&gt;
* &#039;&#039;&#039;Read&#039;&#039;&#039; – Datensatz lesen&lt;br /&gt;
* &#039;&#039;&#039;Update&#039;&#039;&#039; – Datensatz ändern&lt;br /&gt;
* &#039;&#039;&#039;Delete&#039;&#039;&#039; – Datensatz löschen&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
&lt;br /&gt;
Eine neue Entity wird zunächst dem DbContext hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Harnstoff&amp;quot;,&lt;br /&gt;
    Price = 315.00m,&lt;br /&gt;
    Stock = 12000&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erst mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChanges()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
beziehungsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die Änderungen tatsächlich in die Datenbank geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== Read ===&lt;br /&gt;
&lt;br /&gt;
Alle Produkte laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einen Datensatz anhand des Primärschlüssels suchen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products.FindAsync(1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit LINQ filtern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 300)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Update ===&lt;br /&gt;
&lt;br /&gt;
Eine geladene Entity kann direkt geändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products.FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 299.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt über den Change Tracker, welche Property verändert wurde.&lt;br /&gt;
&lt;br /&gt;
=== Delete ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products.FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== LINQ ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Language Integrated Query&#039;&#039;&#039; (LINQ) ist die zentrale Abfragesprache für EF Core.&lt;br /&gt;
&lt;br /&gt;
Eine LINQ-Abfrage wird normalerweise nicht unmittelbar ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Variable enthält zunächst eine Abfragebeschreibung.&lt;br /&gt;
&lt;br /&gt;
Erst durch eine Operation wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ToListAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird die Datenbankabfrage ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Typische LINQ-Operationen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Methode&lt;br /&gt;
&lt;br /&gt;
| ! Bedeutung                              |&lt;br /&gt;
| ---------------------------------------- |&lt;br /&gt;
| Where()                                  |&lt;br /&gt;
| Datensätze filtern                       |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| Select()                                 |&lt;br /&gt;
| Daten projizieren                        |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| OrderBy()                                |&lt;br /&gt;
| aufsteigend sortieren                    |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| OrderByDescending()                      |&lt;br /&gt;
| absteigend sortieren                     |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| ThenBy()                                 |&lt;br /&gt;
| weitere Sortierung                       |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| FirstAsync()                             |&lt;br /&gt;
| ersten Datensatz laden                   |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| FirstOrDefaultAsync()                    |&lt;br /&gt;
| ersten Datensatz oder Standardwert laden |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| SingleAsync()                            |&lt;br /&gt;
| genau einen Datensatz erwarten           |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| AnyAsync()                               |&lt;br /&gt;
| prüfen, ob Datensätze existieren         |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| CountAsync()                             |&lt;br /&gt;
| Datensätze zählen                        |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| SumAsync()                               |&lt;br /&gt;
| Summe berechnen                          |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| AverageAsync()                           |&lt;br /&gt;
| Durchschnitt berechnen                   |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| MaxAsync()                               |&lt;br /&gt;
| Maximum bestimmen                        |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| MinAsync()                               |&lt;br /&gt;
| Minimum bestimmen                        |&lt;br /&gt;
| -                                        |&lt;br /&gt;
| ToListAsync()                            |&lt;br /&gt;
| Ergebnis als Liste laden                 |&lt;br /&gt;
| }                                        |&lt;br /&gt;
&lt;br /&gt;
=== Projektionen ===&lt;br /&gt;
&lt;br /&gt;
Es ist häufig nicht notwendig, vollständige Entities aus der Datenbank zu laden.&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
können nur die benötigten Werte selektiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new&lt;br /&gt;
    {&lt;br /&gt;
        p.Id,&lt;br /&gt;
        p.Name,&lt;br /&gt;
        p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann die übertragene Datenmenge reduzieren und die Performance verbessern.&lt;br /&gt;
&lt;br /&gt;
== Asynchrone Datenbankzugriffe ==&lt;br /&gt;
&lt;br /&gt;
Bei Serveranwendungen sollten Datenbankoperationen nach Möglichkeit asynchron ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = context.Products.ToList();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typische asynchrone Methoden sind:&lt;br /&gt;
&lt;br /&gt;
* ToListAsync()&lt;br /&gt;
* FirstAsync()&lt;br /&gt;
* FirstOrDefaultAsync()&lt;br /&gt;
* SingleAsync()&lt;br /&gt;
* AnyAsync()&lt;br /&gt;
* CountAsync()&lt;br /&gt;
* FindAsync()&lt;br /&gt;
* SaveChangesAsync()&lt;br /&gt;
&lt;br /&gt;
Asynchrone Datenbankzugriffe blockieren den ausführenden Thread nicht während auf die Antwort der Datenbank gewartet wird. Dies ist besonders bei ASP.NET-Core-Anwendungen von Bedeutung.&lt;br /&gt;
&lt;br /&gt;
== Change Tracking ==&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;Change Tracker&#039;&#039;&#039; ist eine zentrale Komponente von EF Core.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext merkt sich standardmäßig Entities, die über ihn geladen wurden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = await context.Products.FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
product.Price = 350;&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim Aufruf von:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesAsync()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ermittelt EF Core, welche Entities und Properties verändert wurden, und erzeugt daraus entsprechende Datenbankbefehle.&lt;br /&gt;
&lt;br /&gt;
=== Entity States ===&lt;br /&gt;
&lt;br /&gt;
EF Core kennt unter anderem folgende Zustände:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Zustand&lt;br /&gt;
&lt;br /&gt;
| ! Bedeutung                                      |&lt;br /&gt;
| ------------------------------------------------ |&lt;br /&gt;
| Detached                                         |&lt;br /&gt;
| Entity wird vom DbContext nicht verfolgt         |&lt;br /&gt;
| -                                                |&lt;br /&gt;
| Unchanged                                        |&lt;br /&gt;
| Entity wird verfolgt, wurde aber nicht verändert |&lt;br /&gt;
| -                                                |&lt;br /&gt;
| Added                                            |&lt;br /&gt;
| Entity soll eingefügt werden                     |&lt;br /&gt;
| -                                                |&lt;br /&gt;
| Modified                                         |&lt;br /&gt;
| Entity wurde verändert                           |&lt;br /&gt;
| -                                                |&lt;br /&gt;
| Deleted                                          |&lt;br /&gt;
| Entity soll gelöscht werden                      |&lt;br /&gt;
| }                                                |&lt;br /&gt;
&lt;br /&gt;
Der Status einer Entity kann abgefragt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var state = context.Entry(product).State;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== No-Tracking-Abfragen ==&lt;br /&gt;
&lt;br /&gt;
Bei reinen Leseoperationen ist das Change Tracking häufig nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
In diesem Fall kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann Speicherverbrauch und Verwaltungsaufwand reduzieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Faustregel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Wenn geladene Daten nur angezeigt und anschließend nicht verändert werden sollen, ist &#039;&#039;&#039;AsNoTracking()&#039;&#039;&#039; häufig sinnvoll.&lt;br /&gt;
&lt;br /&gt;
== Konventionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core versucht das Datenmodell zunächst anhand von Konventionen zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erkennt automatisch:&lt;br /&gt;
&lt;br /&gt;
* Customer als Entity&lt;br /&gt;
* Id als Primärschlüssel&lt;br /&gt;
* Name als Datenbankspalte&lt;br /&gt;
&lt;br /&gt;
Dies wird als &#039;&#039;&#039;Convention-based Configuration&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Data Annotations ==&lt;br /&gt;
&lt;br /&gt;
Das Datenmodell kann mit Attributen genauer konfiguriert werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using System.ComponentModel.DataAnnotations;&lt;br /&gt;
using System.ComponentModel.DataAnnotations.Schema;&lt;br /&gt;
&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
[Key]&lt;br /&gt;
public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
[Required]&lt;br /&gt;
[MaxLength(200)]&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
[Column(TypeName = &amp;quot;decimal(10,2)&amp;quot;)]&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Attribute sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Attribut&lt;br /&gt;
&lt;br /&gt;
| ! Bedeutung                               |&lt;br /&gt;
| ----------------------------------------- |&lt;br /&gt;
| [Key]                                     |&lt;br /&gt;
| Primärschlüssel                           |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [Required]                                |&lt;br /&gt;
| Pflichtfeld                               |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [MaxLength]                               |&lt;br /&gt;
| maximale Länge                            |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [Column]                                  |&lt;br /&gt;
| Datenbankspalte konfigurieren             |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [Table]                                   |&lt;br /&gt;
| Tabellennamen konfigurieren               |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [NotMapped]                               |&lt;br /&gt;
| Property nicht in der Datenbank speichern |&lt;br /&gt;
| -                                         |&lt;br /&gt;
| [ForeignKey]                              |&lt;br /&gt;
| Fremdschlüssel definieren                 |&lt;br /&gt;
| }                                         |&lt;br /&gt;
&lt;br /&gt;
== Fluent API ==&lt;br /&gt;
&lt;br /&gt;
Eine flexiblere Alternative zu Data Annotations ist die &#039;&#039;&#039;Fluent API&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration erfolgt normalerweise in:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
OnModelCreating()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
});&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Fluent API bietet mehr Konfigurationsmöglichkeiten als Data Annotations.&lt;br /&gt;
&lt;br /&gt;
== IEntityTypeConfiguration ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Projekten sollte die Datenbankkonfiguration nicht vollständig im DbContext abgelegt werden.&lt;br /&gt;
&lt;br /&gt;
Stattdessen können separate Konfigurationsklassen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
using Microsoft.EntityFrameworkCore;&lt;br /&gt;
using Microsoft.EntityFrameworkCore.Metadata.Builders;&lt;br /&gt;
&lt;br /&gt;
public class ProductConfiguration&lt;br /&gt;
: IEntityTypeConfiguration&amp;lt;Product&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
public void Configure(EntityTypeBuilder&amp;lt;Product&amp;gt; builder)&lt;br /&gt;
{&lt;br /&gt;
builder.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Name)&lt;br /&gt;
        .IsRequired()&lt;br /&gt;
        .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
    builder.Property(p =&amp;gt; p.Price)&lt;br /&gt;
        .HasPrecision(10, 2);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im DbContext:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void OnModelCreating(ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.ApplyConfigurationsFromAssembly(&lt;br /&gt;
        typeof(ApplicationDbContext).Assembly);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies sorgt insbesondere bei umfangreichen Datenmodellen für eine bessere Struktur.&lt;br /&gt;
&lt;br /&gt;
== Beziehungen zwischen Entities ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt die üblichen Beziehungen relationaler Datenbanken.&lt;br /&gt;
&lt;br /&gt;
Dazu gehören:&lt;br /&gt;
&lt;br /&gt;
* 1:1&lt;br /&gt;
* 1:n&lt;br /&gt;
* n:m&lt;br /&gt;
&lt;br /&gt;
== 1:n-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Kunde kann beispielsweise mehrere Bestellungen besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Order&amp;gt; Orders { get; set; }&lt;br /&gt;
    = new List&amp;lt;Order&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Bestellung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Order&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime OrderDate { get; set; }&lt;br /&gt;
&lt;br /&gt;
public int CustomerId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public Customer Customer { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
CustomerId&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der Fremdschlüssel.&lt;br /&gt;
&lt;br /&gt;
Die Navigation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
Customer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
enthält das zugehörige Kundenobjekt.&lt;br /&gt;
&lt;br /&gt;
Die Gegenrichtung wird durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ICollection&amp;lt;Order&amp;gt; Orders&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
abgebildet.&lt;br /&gt;
&lt;br /&gt;
== 1:1-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann ein Benutzer genau ein Profil besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class User&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string UserName { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public UserProfile? Profile { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class UserProfile&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public int UserId { get; set; }&lt;br /&gt;
&lt;br /&gt;
public User User { get; set; } = null!;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Beziehung kann mit der Fluent API explizit definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;User&amp;gt;()&lt;br /&gt;
    .HasOne(u =&amp;gt; u.Profile)&lt;br /&gt;
    .WithOne(p =&amp;gt; p.User)&lt;br /&gt;
    .HasForeignKey&amp;lt;UserProfile&amp;gt;(p =&amp;gt; p.UserId);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== n:m-Beziehung ==&lt;br /&gt;
&lt;br /&gt;
Ein Produkt kann beispielsweise mehreren Kategorien angehören und eine Kategorie kann mehrere Produkte enthalten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Category&amp;gt; Categories { get; set; }&lt;br /&gt;
    = new List&amp;lt;Category&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Category&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public ICollection&amp;lt;Product&amp;gt; Products { get; set; }&lt;br /&gt;
    = new List&amp;lt;Product&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core kann für einfache n:m-Beziehungen automatisch eine Zwischentabelle erstellen.&lt;br /&gt;
&lt;br /&gt;
Wenn die Beziehung selbst weitere Informationen enthalten soll, sollte hingegen eine eigene Entity für die Zwischentabelle erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Product&lt;br /&gt;
   |&lt;br /&gt;
   | 1:n&lt;br /&gt;
   |&lt;br /&gt;
ProductCategory&lt;br /&gt;
   |&lt;br /&gt;
   | n:1&lt;br /&gt;
   |&lt;br /&gt;
Category&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Laden zusammengehöriger Daten ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterscheidet verschiedene Strategien für das Laden von Beziehungen.&lt;br /&gt;
&lt;br /&gt;
=== Eager Loading ===&lt;br /&gt;
&lt;br /&gt;
Beim Eager Loading werden zusammengehörige Daten direkt mit der ursprünglichen Abfrage geladen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Ebenen können mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
ThenInclude()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
geladen werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var orders = await context.Orders&lt;br /&gt;
    .Include(o =&amp;gt; o.Customer)&lt;br /&gt;
    .Include(o =&amp;gt; o.Positions)&lt;br /&gt;
        .ThenInclude(p =&amp;gt; p.Product)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Explicit Loading ===&lt;br /&gt;
&lt;br /&gt;
Zusammengehörige Daten können gezielt nachgeladen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers.FindAsync(1);&lt;br /&gt;
&lt;br /&gt;
if (customer != null)&lt;br /&gt;
{&lt;br /&gt;
await context.Entry(customer)&lt;br /&gt;
.Collection(c =&amp;gt; c.Orders)&lt;br /&gt;
.LoadAsync();&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lazy Loading ===&lt;br /&gt;
&lt;br /&gt;
Beim Lazy Loading werden Beziehungen erst beim Zugriff auf eine Navigation Property geladen.&lt;br /&gt;
&lt;br /&gt;
Dies kann praktisch sein, birgt aber die Gefahr einer großen Anzahl unbemerkter Datenbankabfragen.&lt;br /&gt;
&lt;br /&gt;
Insbesondere das sogenannte &#039;&#039;&#039;N+1-Problem&#039;&#039;&#039; kann dadurch entstehen.&lt;br /&gt;
&lt;br /&gt;
Lazy Loading sollte daher bewusst eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== Das N+1-Problem ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, zunächst werden 100 Kunden geladen.&lt;br /&gt;
&lt;br /&gt;
Danach wird für jeden Kunden separat dessen Bestellliste abgefragt.&lt;br /&gt;
&lt;br /&gt;
Das kann zu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1 Abfrage für die Kunden&lt;br /&gt;
+&lt;br /&gt;
100 Abfragen für die Bestellungen&lt;br /&gt;
=&lt;br /&gt;
101 Datenbankabfragen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
führen.&lt;br /&gt;
&lt;br /&gt;
Bei großen Datenmengen kann dies erhebliche Performanceprobleme verursachen.&lt;br /&gt;
&lt;br /&gt;
Eine Alternative ist beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder eine gezielte Projektion.&lt;br /&gt;
&lt;br /&gt;
== Migrationen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Migrations&#039;&#039;&#039; ermöglichen es, Änderungen des C#-Datenmodells kontrolliert auf das Datenbankschema zu übertragen.&lt;br /&gt;
&lt;br /&gt;
Angenommen, eine Entity wird um eine Property erweitert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public string Description { get; set; } = string.Empty;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann kann eine neue Migration erzeugt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations add AddProductDescription&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erstellt daraufhin eine Migrationsklasse.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht könnte diese so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
protected override void Up(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
    migrationBuilder.AddColumn&amp;lt;string&amp;gt;(&lt;br /&gt;
        name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
        table: &amp;quot;Products&amp;quot;,&lt;br /&gt;
        type: &amp;quot;nvarchar(max)&amp;quot;,&lt;br /&gt;
        nullable: false,&lt;br /&gt;
        defaultValue: &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
protected override void Down(MigrationBuilder migrationBuilder)&lt;br /&gt;
{&lt;br /&gt;
migrationBuilder.DropColumn(&lt;br /&gt;
name: &amp;quot;Description&amp;quot;,&lt;br /&gt;
table: &amp;quot;Products&amp;quot;);&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Migration kann anschließend auf die Datenbank angewendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef database update&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Migrationsbefehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Befehl&lt;br /&gt;
&lt;br /&gt;
| ! Funktion                                        |&lt;br /&gt;
| ------------------------------------------------- |&lt;br /&gt;
| dotnet ef migrations add Name                     |&lt;br /&gt;
| neue Migration erzeugen                           |&lt;br /&gt;
| -                                                 |&lt;br /&gt;
| dotnet ef migrations list                         |&lt;br /&gt;
| vorhandene Migrationen anzeigen                   |&lt;br /&gt;
| -                                                 |&lt;br /&gt;
| dotnet ef migrations remove                       |&lt;br /&gt;
| letzte noch nicht angewendete Migration entfernen |&lt;br /&gt;
| -                                                 |&lt;br /&gt;
| dotnet ef database update                         |&lt;br /&gt;
| Datenbank auf neuesten Stand bringen              |&lt;br /&gt;
| -                                                 |&lt;br /&gt;
| dotnet ef database update MigrationName           |&lt;br /&gt;
| Datenbank auf bestimmte Migration setzen          |&lt;br /&gt;
| -                                                 |&lt;br /&gt;
| dotnet ef migrations script                       |&lt;br /&gt;
| SQL-Skript für Migrationen erzeugen               |&lt;br /&gt;
| }                                                 |&lt;br /&gt;
&lt;br /&gt;
== Migrationen in Produktivsystemen ==&lt;br /&gt;
&lt;br /&gt;
Migrationen sollten in Produktivumgebungen kontrolliert eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das automatische Ausführen aller Migrationen beim Programmstart ist zwar technisch möglich, bei geschäftskritischen Systemen jedoch nicht immer empfehlenswert.&lt;br /&gt;
&lt;br /&gt;
Ein kontrollierter Deployment-Prozess bietet mehrere Vorteile:&lt;br /&gt;
&lt;br /&gt;
* Änderungen können vorab geprüft werden.&lt;br /&gt;
* SQL-Skripte können dokumentiert werden.&lt;br /&gt;
* Backups können vor Schemaänderungen durchgeführt werden.&lt;br /&gt;
* lange laufende Migrationen können geplant werden.&lt;br /&gt;
* Datenbankberechtigungen können restriktiver gehalten werden.&lt;br /&gt;
&lt;br /&gt;
Für produktive Systeme kann beispielsweise ein SQL-Skript erstellt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef migrations script&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Code First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Code-First-Ansatz&#039;&#039;&#039; wird zunächst das Datenmodell in C# definiert.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C# Entity-Klassen&lt;br /&gt;
       ↓&lt;br /&gt;
EF Core Modell&lt;br /&gt;
       ↓&lt;br /&gt;
Migration&lt;br /&gt;
       ↓&lt;br /&gt;
Datenbankschema&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code First ist besonders bei neu entwickelten Anwendungen verbreitet.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&lt;br /&gt;
* Datenmodell befindet sich im Quellcode.&lt;br /&gt;
* Änderungen können mit Git versioniert werden.&lt;br /&gt;
* Migrationen dokumentieren Schemaänderungen.&lt;br /&gt;
* gute Integration in CI/CD-Prozesse.&lt;br /&gt;
&lt;br /&gt;
== Database First ==&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;Database-First-Ansatz&#039;&#039;&#039; existiert die Datenbank bereits.&lt;br /&gt;
&lt;br /&gt;
EF Core kann daraus DbContext und Entity-Klassen erzeugen.&lt;br /&gt;
&lt;br /&gt;
Dieser Vorgang wird als &#039;&#039;&#039;Reverse Engineering&#039;&#039;&#039; oder &#039;&#039;&#039;Scaffolding&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
dotnet ef dbcontext scaffold &amp;quot;CONNECTION_STRING&amp;quot; Microsoft.EntityFrameworkCore.SqlServer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist beispielsweise bei der Modernisierung bestehender Anwendungen interessant, wenn bereits eine produktive Datenbank vorhanden ist.&lt;br /&gt;
&lt;br /&gt;
== SaveChanges ==&lt;br /&gt;
&lt;br /&gt;
Mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
context.SaveChanges();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden vorgemerkte Änderungen an die Datenbank übertragen.&lt;br /&gt;
&lt;br /&gt;
EF Core untersucht dazu den Change Tracker und generiert entsprechende Befehle.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Added&lt;br /&gt;
    → INSERT&lt;br /&gt;
&lt;br /&gt;
Modified&lt;br /&gt;
    → UPDATE&lt;br /&gt;
&lt;br /&gt;
Deleted&lt;br /&gt;
    → DELETE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Transaktionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core verwendet bei einem einzelnen Aufruf von SaveChanges normalerweise eine Transaktion, sofern der verwendete Datenbankprovider Transaktionen unterstützt.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Vorgängen kann eine Transaktion auch explizit verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var transaction =&lt;br /&gt;
    await context.Database.BeginTransactionAsync();&lt;br /&gt;
&lt;br /&gt;
try&lt;br /&gt;
{&lt;br /&gt;
var customer = new Customer&lt;br /&gt;
{&lt;br /&gt;
Name = &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
context.Customers.Add(customer);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
var order = new Order&lt;br /&gt;
{&lt;br /&gt;
    CustomerId = customer.Id,&lt;br /&gt;
    OrderDate = DateTime.UtcNow&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Orders.Add(order);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
&lt;br /&gt;
await transaction.CommitAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
catch&lt;br /&gt;
{&lt;br /&gt;
await transaction.RollbackAsync();&lt;br /&gt;
throw;&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird sichergestellt, dass entweder alle Operationen erfolgreich durchgeführt werden oder keine.&lt;br /&gt;
&lt;br /&gt;
== Optimistic Concurrency ==&lt;br /&gt;
&lt;br /&gt;
Mehrere Benutzer können gleichzeitig denselben Datensatz bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Benutzer A liest Datensatz&lt;br /&gt;
Benutzer B liest Datensatz&lt;br /&gt;
Benutzer A speichert Änderung&lt;br /&gt;
Benutzer B speichert Änderung&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ohne zusätzliche Maßnahmen könnte Benutzer B die Änderung von Benutzer A überschreiben.&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt deshalb &#039;&#039;&#039;Optimistic Concurrency&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein SQL-Server-Modell kann beispielsweise eine RowVersion verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public byte[] RowVersion { get; set; } = Array.Empty&amp;lt;byte&amp;gt;();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .Property(p =&amp;gt; p.RowVersion)&lt;br /&gt;
    .IsRowVersion();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurde der Datensatz zwischenzeitlich von einem anderen Prozess verändert, kann beim Speichern eine:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
DbUpdateConcurrencyException&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ausgelöst werden.&lt;br /&gt;
&lt;br /&gt;
Die Anwendung muss anschließend entscheiden, wie mit dem Konflikt umgegangen wird.&lt;br /&gt;
&lt;br /&gt;
== ExecuteUpdate und ExecuteDelete ==&lt;br /&gt;
&lt;br /&gt;
Bei größeren Datenänderungen ist es nicht immer sinnvoll, zunächst alle Entities zu laden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise wäre folgende Vorgehensweise ineffizient:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&lt;br /&gt;
foreach (var product in products)&lt;br /&gt;
{&lt;br /&gt;
product.Price *= 1.05m;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt direkte Mengenoperationen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;lt; 100)&lt;br /&gt;
    .ExecuteUpdateAsync(setters =&amp;gt; setters&lt;br /&gt;
        .SetProperty(&lt;br /&gt;
            p =&amp;gt; p.Price,&lt;br /&gt;
            p =&amp;gt; p.Price * 1.05m));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datensätze können entsprechend direkt gelöscht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Stock &amp;lt;= 0)&lt;br /&gt;
    .ExecuteDeleteAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Vorteil besteht darin, dass die betroffenen Entities nicht zunächst vollständig in den Arbeitsspeicher geladen werden müssen.&lt;br /&gt;
&lt;br /&gt;
== Raw SQL ==&lt;br /&gt;
&lt;br /&gt;
Obwohl EF Core hauptsächlich über LINQ verwendet wird, können auch direkte SQL-Abfragen ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .FromSqlInterpolated(&lt;br /&gt;
        $&amp;quot;SELECT * FROM Products WHERE Price &amp;gt; {minimumPrice}&amp;quot;)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Parameter sollten nicht durch String-Verkettung in SQL-Befehle eingebaut werden.&lt;br /&gt;
&lt;br /&gt;
Unsicher wäre beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var sql =&lt;br /&gt;
    &amp;quot;SELECT * FROM Products WHERE Name = &#039;&amp;quot; +&lt;br /&gt;
    userInput +&lt;br /&gt;
    &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies kann SQL-Injection-Schwachstellen verursachen.&lt;br /&gt;
&lt;br /&gt;
Stattdessen sollten parameterisierte APIs verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Globale Query Filter ==&lt;br /&gt;
&lt;br /&gt;
Ein globaler Query Filter wird automatisch auf Abfragen einer bestimmten Entity angewendet.&lt;br /&gt;
&lt;br /&gt;
Ein typischer Anwendungsfall ist &#039;&#039;&#039;Soft Delete&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Entity:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public bool IsDeleted { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konfiguration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasQueryFilter(p =&amp;gt; !p.IsDeleted);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine normale Abfrage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
liefert dadurch standardmäßig nur Datensätze, bei denen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
IsDeleted = false&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gilt.&lt;br /&gt;
&lt;br /&gt;
Ein Filter kann bei Bedarf ignoriert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .IgnoreQueryFilters()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Globale Filter werden ebenfalls häufig bei Multi-Tenant-Anwendungen eingesetzt.&lt;br /&gt;
&lt;br /&gt;
== Shadow Properties ==&lt;br /&gt;
&lt;br /&gt;
EF Core kann Eigenschaften verwalten, die in der eigentlichen Entity-Klasse nicht existieren.&lt;br /&gt;
&lt;br /&gt;
Diese werden &#039;&#039;&#039;Shadow Properties&#039;&#039;&#039; genannt.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann ein Fremdschlüssel nur im EF-Core-Modell existieren, ohne als C#-Property definiert zu sein.&lt;br /&gt;
&lt;br /&gt;
Shadow Properties können nützlich sein, sollten aber bewusst eingesetzt werden, da sie im Anwendungscode weniger sichtbar sind.&lt;br /&gt;
&lt;br /&gt;
== Value Conversions ==&lt;br /&gt;
&lt;br /&gt;
Mit Value Conversions können .NET-Typen vor dem Speichern in einen anderen Datenbanktyp umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann ein Enum als String gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Order&amp;gt;()&lt;br /&gt;
    .Property(o =&amp;gt; o.Status)&lt;br /&gt;
    .HasConversion&amp;lt;string&amp;gt;();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Statt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
0&lt;br /&gt;
1&lt;br /&gt;
2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
können dann beispielsweise Werte wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
New&lt;br /&gt;
Processing&lt;br /&gt;
Completed&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in der Datenbank gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
== Komplexe Typen ==&lt;br /&gt;
&lt;br /&gt;
Neuere EF-Core-Versionen unterstützen &#039;&#039;&#039;Complex Types&#039;&#039;&#039; zur Modellierung von strukturierten Werten, die nicht als eigenständige Entity behandelt werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Address&lt;br /&gt;
{&lt;br /&gt;
    public string Street { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string City { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public string PostalCode { get; set; } = string.Empty;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Entity kann diesen Typ verwenden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Customer&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public Address Address { get; set; } = new();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Solche Strukturen eignen sich beispielsweise für:&lt;br /&gt;
&lt;br /&gt;
* Adressen&lt;br /&gt;
* Geldbeträge mit Zusatzinformationen&lt;br /&gt;
* Koordinaten&lt;br /&gt;
* Kontaktdaten&lt;br /&gt;
* wiederverwendbare Werteobjekte&lt;br /&gt;
&lt;br /&gt;
== Indizes ==&lt;br /&gt;
&lt;br /&gt;
Indizes können mit der Fluent API angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Product&amp;gt;()&lt;br /&gt;
    .HasIndex(p =&amp;gt; p.Name);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein eindeutiger Index:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
modelBuilder.Entity&amp;lt;Customer&amp;gt;()&lt;br /&gt;
    .HasIndex(c =&amp;gt; c.Email)&lt;br /&gt;
    .IsUnique();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Indizes können Datenbankabfragen erheblich beschleunigen. Sie erhöhen jedoch den Speicherbedarf und den Aufwand bei INSERT-, UPDATE- und DELETE-Operationen.&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Indizes ist daher eine Aufgabe des Datenbankdesigns und sollte nicht allein EF Core überlassen werden.&lt;br /&gt;
&lt;br /&gt;
== Performance ==&lt;br /&gt;
&lt;br /&gt;
EF Core abstrahiert viele Datenbankoperationen. Dadurch besteht die Gefahr, dass teure Datenbankabfragen im C#-Code harmlos aussehen.&lt;br /&gt;
&lt;br /&gt;
Bei Performanceproblemen sollten insbesondere folgende Bereiche geprüft werden:&lt;br /&gt;
&lt;br /&gt;
* Anzahl der Datenbankabfragen&lt;br /&gt;
* erzeugtes SQL&lt;br /&gt;
* verwendete Indizes&lt;br /&gt;
* geladene Datenmenge&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Include()-Aufrufe&lt;br /&gt;
* große Objektgraphen&lt;br /&gt;
* N+1-Probleme&lt;br /&gt;
* unnötige Roundtrips zur Datenbank&lt;br /&gt;
&lt;br /&gt;
== Nur benötigte Daten laden ==&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wenn anschließend nur der Name benötigt wird.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder mit DTO:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Select(p =&amp;gt; new ProductListDto&lt;br /&gt;
    {&lt;br /&gt;
        Id = p.Id,&lt;br /&gt;
        Name = p.Name,&lt;br /&gt;
        Price = p.Price&lt;br /&gt;
    })&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Paging ==&lt;br /&gt;
&lt;br /&gt;
Große Datenmengen sollten nicht vollständig geladen werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Id)&lt;br /&gt;
    .Skip(100)&lt;br /&gt;
    .Take(50)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies lädt lediglich 50 Datensätze ab Position 101.&lt;br /&gt;
&lt;br /&gt;
Für sehr große Datenmengen kann je nach Anwendungsfall ein Keyset- beziehungsweise Cursor-basiertes Paging effizienter sein als sehr große Skip-Werte.&lt;br /&gt;
&lt;br /&gt;
== Split Queries ==&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Include-Abfragen kann ein einzelnes großes SQL-Statement sehr viele redundante Datensätze erzeugen.&lt;br /&gt;
&lt;br /&gt;
EF Core bietet dafür Split Queries:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customers = await context.Customers&lt;br /&gt;
    .Include(c =&amp;gt; c.Orders)&lt;br /&gt;
    .AsSplitQuery()&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei werden mehrere Datenbankabfragen erzeugt.&lt;br /&gt;
&lt;br /&gt;
Ob eine Single Query oder Split Query besser ist, hängt vom konkreten Datenmodell und der Datenmenge ab.&lt;br /&gt;
&lt;br /&gt;
== SQL einer Abfrage anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Das erzeugte SQL einer LINQ-Abfrage kann zu Diagnosezwecken angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var query = context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&lt;br /&gt;
var sql = query.ToQueryString(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist bei Performanceanalysen und bei der Fehlersuche besonders hilfreich.&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
EF Core lässt sich in das .NET-Logging-System integrieren.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
options&lt;br /&gt;
    .UseSqlServer(connectionString)&lt;br /&gt;
    .LogTo(Console.WriteLine);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während der Entwicklung kann dadurch unter anderem sichtbar gemacht werden:&lt;br /&gt;
&lt;br /&gt;
* welche SQL-Befehle ausgeführt werden&lt;br /&gt;
* welche Parameter verwendet werden&lt;br /&gt;
* wie lange Operationen dauern&lt;br /&gt;
* welche Warnungen EF Core erzeugt&lt;br /&gt;
&lt;br /&gt;
Sensible Daten sollten in produktiven Logs nicht unkontrolliert ausgegeben werden.&lt;br /&gt;
&lt;br /&gt;
== DbContext-Lebensdauer ==&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist als kurzlebige &#039;&#039;&#039;Unit of Work&#039;&#039;&#039; gedacht.&lt;br /&gt;
&lt;br /&gt;
Typischer Ablauf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DbContext erzeugen&lt;br /&gt;
        ↓&lt;br /&gt;
Entities laden&lt;br /&gt;
        ↓&lt;br /&gt;
Änderungen durchführen&lt;br /&gt;
        ↓&lt;br /&gt;
SaveChanges&lt;br /&gt;
        ↓&lt;br /&gt;
DbContext freigeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte normalerweise nicht:&lt;br /&gt;
&lt;br /&gt;
* global gespeichert werden&lt;br /&gt;
* als Singleton registriert werden&lt;br /&gt;
* über sehr lange Zeiträume aktiv bleiben&lt;br /&gt;
* gleichzeitig von mehreren Threads verwendet werden&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbContext ist nicht threadsicher.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== DbContextFactory ==&lt;br /&gt;
&lt;br /&gt;
In Anwendungen, in denen Dependency-Injection-Scopes nicht direkt einem Vorgang entsprechen, kann eine DbContextFactory sinnvoll sein.&lt;br /&gt;
&lt;br /&gt;
Dies betrifft beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Blazor&lt;br /&gt;
* Background Services&lt;br /&gt;
* Desktop-Anwendungen&lt;br /&gt;
* Worker&lt;br /&gt;
* parallele Verarbeitung&lt;br /&gt;
&lt;br /&gt;
Registrierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(connectionString));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await using var context =&lt;br /&gt;
    await dbContextFactory.CreateDbContextAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gerade bei Blazor-Anwendungen ist eine bewusste Verwaltung der DbContext-Lebensdauer wichtig, da ein Benutzer-Circuit wesentlich länger existieren kann als ein klassischer HTTP-Request.&lt;br /&gt;
&lt;br /&gt;
== Repository Pattern ==&lt;br /&gt;
&lt;br /&gt;
EF Core wird häufig zusammen mit dem &#039;&#039;&#039;Repository Pattern&#039;&#039;&#039; eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public interface IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync();&lt;br /&gt;
&lt;br /&gt;
Task AddAsync(Product product);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine mögliche Implementierung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductRepository : IProductRepository&lt;br /&gt;
{&lt;br /&gt;
    private readonly ApplicationDbContext _context;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductRepository(&lt;br /&gt;
    ApplicationDbContext context)&lt;br /&gt;
{&lt;br /&gt;
    _context = context;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;Product?&amp;gt; GetByIdAsync(int id)&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products.FindAsync(id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetAllAsync()&lt;br /&gt;
{&lt;br /&gt;
    return await _context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task AddAsync(Product product)&lt;br /&gt;
{&lt;br /&gt;
    _context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
    await _context.SaveChangesAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ob ein zusätzliches Repository sinnvoll ist, hängt vom Projekt ab.&lt;br /&gt;
&lt;br /&gt;
EF Core selbst implementiert bereits viele Eigenschaften der Repository- und Unit-of-Work-Patterns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! EF-Core-Komponente&lt;br /&gt;
&lt;br /&gt;
| ! Ähnliches Pattern |&lt;br /&gt;
| ------------------- |&lt;br /&gt;
| DbSet               |&lt;br /&gt;
| Repository          |&lt;br /&gt;
| -                   |&lt;br /&gt;
| DbContext           |&lt;br /&gt;
| Unit of Work        |&lt;br /&gt;
| }                   |&lt;br /&gt;
&lt;br /&gt;
Eine zusätzliche Repository-Schicht sollte deshalb einen konkreten architektonischen Nutzen bieten und nicht nur Methoden wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
GetAll()&lt;br /&gt;
GetById()&lt;br /&gt;
Add()&lt;br /&gt;
Update()&lt;br /&gt;
Delete()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
blind um DbSet herumwickeln.&lt;br /&gt;
&lt;br /&gt;
== DTOs und Entities ==&lt;br /&gt;
&lt;br /&gt;
Datenbank-Entities sollten insbesondere in Webanwendungen nicht automatisch als API-Modelle verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Besser ist häufig eine Trennung zwischen:&lt;br /&gt;
&lt;br /&gt;
* Entity&lt;br /&gt;
* DTO&lt;br /&gt;
* ViewModel&lt;br /&gt;
* Request Model&lt;br /&gt;
* Response Model&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductDto&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies verhindert unter anderem, dass interne Datenbankstrukturen unnötig Teil einer öffentlichen API werden.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit ==&lt;br /&gt;
&lt;br /&gt;
EF Core schützt bei normaler Verwendung von LINQ und parametrisierten Datenbankoperationen weitgehend vor SQL Injection.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var customer = await context.Customers&lt;br /&gt;
    .Where(c =&amp;gt; c.Name == userInput)&lt;br /&gt;
    .FirstOrDefaultAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
EF Core erzeugt für:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
userInput&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
einen Datenbankparameter.&lt;br /&gt;
&lt;br /&gt;
Trotzdem müssen weitere Sicherheitsaspekte berücksichtigt werden.&lt;br /&gt;
&lt;br /&gt;
Dazu gehören:&lt;br /&gt;
&lt;br /&gt;
* Connection Strings nicht im Quellcode speichern&lt;br /&gt;
* Passwörter über Secret Stores beziehungsweise sichere Konfiguration verwalten&lt;br /&gt;
* Datenbankbenutzer nach dem Least-Privilege-Prinzip konfigurieren&lt;br /&gt;
* Raw SQL ausschließlich parameterisiert verwenden&lt;br /&gt;
* Benutzereingaben validieren&lt;br /&gt;
* Fehlerdetails nicht ungefiltert an Benutzer ausgeben&lt;br /&gt;
* sensible Daten nicht unkontrolliert protokollieren&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
EF-Core-Code kann auf unterschiedliche Weise getestet werden.&lt;br /&gt;
&lt;br /&gt;
Mögliche Varianten sind:&lt;br /&gt;
&lt;br /&gt;
* echte Testdatenbank&lt;br /&gt;
* temporäre Datenbank&lt;br /&gt;
* SQLite In-Memory&lt;br /&gt;
* EF-Core-InMemory-Provider&lt;br /&gt;
* Testcontainer&lt;br /&gt;
&lt;br /&gt;
=== InMemory Provider ===&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt einen InMemory-Provider.&lt;br /&gt;
&lt;br /&gt;
Dieser kann für bestimmte einfache Tests hilfreich sein, verhält sich jedoch nicht wie eine relationale Datenbank.&lt;br /&gt;
&lt;br /&gt;
Insbesondere fehlen oder unterscheiden sich beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* SQL-Ausführung&lt;br /&gt;
* relationale Constraints&lt;br /&gt;
* manche Transaktionsmechanismen&lt;br /&gt;
* providerabhängige Funktionen&lt;br /&gt;
* Datenbankfunktionen&lt;br /&gt;
* SQL-Übersetzung&lt;br /&gt;
&lt;br /&gt;
Tests, die mit dem InMemory-Provider erfolgreich sind, müssen deshalb nicht automatisch mit SQL Server, PostgreSQL oder MariaDB funktionieren.&lt;br /&gt;
&lt;br /&gt;
=== SQLite In-Memory ===&lt;br /&gt;
&lt;br /&gt;
Für relationale Tests kann SQLite im Arbeitsspeicher häufig eine bessere Annäherung darstellen.&lt;br /&gt;
&lt;br /&gt;
Aber auch SQLite verhält sich nicht vollständig identisch zu anderen Datenbanksystemen.&lt;br /&gt;
&lt;br /&gt;
=== Integrationstests ===&lt;br /&gt;
&lt;br /&gt;
Für kritische Datenbanklogik sind Tests gegen dasselbe Datenbanksystem wie in der Produktion häufig die zuverlässigste Variante.&lt;br /&gt;
&lt;br /&gt;
Container-Technologien können dafür automatisiert temporäre Datenbanken bereitstellen.&lt;br /&gt;
&lt;br /&gt;
== Interceptors ==&lt;br /&gt;
&lt;br /&gt;
EF Core unterstützt &#039;&#039;&#039;Interceptors&#039;&#039;&#039;, mit denen Datenbankoperationen abgefangen beziehungsweise erweitert werden können.&lt;br /&gt;
&lt;br /&gt;
Mögliche Anwendungsfälle sind:&lt;br /&gt;
&lt;br /&gt;
* Logging&lt;br /&gt;
* Auditing&lt;br /&gt;
* Analyse von SQL-Befehlen&lt;br /&gt;
* automatische Zusatzwerte&lt;br /&gt;
* Überwachung von SaveChanges&lt;br /&gt;
* Performance-Messungen&lt;br /&gt;
&lt;br /&gt;
Ein Beispiel ist ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
SaveChangesInterceptor&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit dem Änderungen vor oder nach SaveChanges verarbeitet werden können.&lt;br /&gt;
&lt;br /&gt;
== Auditing ==&lt;br /&gt;
&lt;br /&gt;
Viele Anwendungen müssen protokollieren, wann Daten erstellt oder verändert wurden.&lt;br /&gt;
&lt;br /&gt;
Eine Basisklasse könnte beispielsweise enthalten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public abstract class EntityBase&lt;br /&gt;
{&lt;br /&gt;
    public DateTime CreatedAt { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DateTime? ModifiedAt { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Werte können beispielsweise vor SaveChanges automatisch gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Auditinformationen können sein:&lt;br /&gt;
&lt;br /&gt;
* CreatedBy&lt;br /&gt;
* ModifiedBy&lt;br /&gt;
* CreatedAt&lt;br /&gt;
* ModifiedAt&lt;br /&gt;
* DeletedAt&lt;br /&gt;
* DeletedBy&lt;br /&gt;
&lt;br /&gt;
== Soft Delete ==&lt;br /&gt;
&lt;br /&gt;
Beim Soft Delete wird ein Datensatz nicht physisch gelöscht.&lt;br /&gt;
&lt;br /&gt;
Stattdessen wird beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IsDeleted = true&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&lt;br /&gt;
* Daten können wiederhergestellt werden.&lt;br /&gt;
* Beziehungen bleiben nachvollziehbar.&lt;br /&gt;
* historische Informationen bleiben erhalten.&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Datenbank wächst weiter.&lt;br /&gt;
* Abfragen müssen gelöschte Datensätze berücksichtigen.&lt;br /&gt;
* Unique Constraints können komplizierter werden.&lt;br /&gt;
* Datenschutzanforderungen können trotzdem eine echte Löschung verlangen.&lt;br /&gt;
&lt;br /&gt;
In EF Core werden Soft Deletes häufig mit globalen Query Filtern kombiniert.&lt;br /&gt;
&lt;br /&gt;
== Datenbankdesign bleibt wichtig ==&lt;br /&gt;
&lt;br /&gt;
EF Core ersetzt kein gutes Datenbankdesign.&lt;br /&gt;
&lt;br /&gt;
Auch bei Verwendung eines ORM müssen Entwickler grundlegende Datenbankkonzepte verstehen.&lt;br /&gt;
&lt;br /&gt;
Dazu gehören insbesondere:&lt;br /&gt;
&lt;br /&gt;
* Primärschlüssel&lt;br /&gt;
* Fremdschlüssel&lt;br /&gt;
* Normalisierung&lt;br /&gt;
* Constraints&lt;br /&gt;
* Indizes&lt;br /&gt;
* Transaktionen&lt;br /&gt;
* Isolation Levels&lt;br /&gt;
* Deadlocks&lt;br /&gt;
* Query-Pläne&lt;br /&gt;
* Joins&lt;br /&gt;
* Datenbankdatentypen&lt;br /&gt;
&lt;br /&gt;
Ein schlecht aufgebautes Datenmodell wird durch EF Core nicht automatisch performant oder konsistent.&lt;br /&gt;
&lt;br /&gt;
== EF Core und SQL ==&lt;br /&gt;
&lt;br /&gt;
EF Core macht SQL-Kenntnisse nicht überflüssig.&lt;br /&gt;
&lt;br /&gt;
Für kleine Anwendungen kann es lange möglich sein, nahezu ausschließlich mit LINQ zu arbeiten.&lt;br /&gt;
&lt;br /&gt;
Bei komplexeren Anwendungen sind SQL-Kenntnisse jedoch wichtig, um:&lt;br /&gt;
&lt;br /&gt;
* generierte Queries zu verstehen&lt;br /&gt;
* Performanceprobleme zu analysieren&lt;br /&gt;
* Indizes zu planen&lt;br /&gt;
* JOINs zu beurteilen&lt;br /&gt;
* Ausführungspläne zu lesen&lt;br /&gt;
* Datenbankprobleme zu diagnostizieren&lt;br /&gt;
* Migrationen zu überprüfen&lt;br /&gt;
&lt;br /&gt;
Die Kombination aus guten EF-Core- und SQL-Kenntnissen ist daher besonders wertvoll.&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber ADO.NET ==&lt;br /&gt;
&lt;br /&gt;
ADO.NET arbeitet wesentlich näher an der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise muss bei ADO.NET häufig:&lt;br /&gt;
&lt;br /&gt;
* SQL manuell geschrieben&lt;br /&gt;
* eine Connection geöffnet&lt;br /&gt;
* ein Command erzeugt&lt;br /&gt;
* Parameter definiert&lt;br /&gt;
* ein DataReader verarbeitet&lt;br /&gt;
* das Ergebnis auf Objekte gemappt&lt;br /&gt;
&lt;br /&gt;
werden.&lt;br /&gt;
&lt;br /&gt;
EF Core automatisiert einen großen Teil dieser Aufgaben.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! EF Core&lt;br /&gt;
&lt;br /&gt;
| ! ADO.NET                     |&lt;br /&gt;
| ----------------------------- |&lt;br /&gt;
| hohe Abstraktion              |&lt;br /&gt;
| niedrige Abstraktion          |&lt;br /&gt;
| -                             |&lt;br /&gt;
| LINQ                          |&lt;br /&gt;
| überwiegend SQL               |&lt;br /&gt;
| -                             |&lt;br /&gt;
| automatisches Mapping         |&lt;br /&gt;
| manuelles Mapping             |&lt;br /&gt;
| -                             |&lt;br /&gt;
| Change Tracking               |&lt;br /&gt;
| normalerweise manuell         |&lt;br /&gt;
| -                             |&lt;br /&gt;
| Migrationen                   |&lt;br /&gt;
| nicht Bestandteil von ADO.NET |&lt;br /&gt;
| -                             |&lt;br /&gt;
| schnelle Entwicklung          |&lt;br /&gt;
| maximale Kontrolle            |&lt;br /&gt;
| }                             |&lt;br /&gt;
&lt;br /&gt;
ADO.NET kann sinnvoll sein, wenn maximale Kontrolle über jede Datenbankoperation erforderlich ist.&lt;br /&gt;
&lt;br /&gt;
== EF Core gegenüber Dapper ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dapper&#039;&#039;&#039; ist ein sogenannter Micro-ORM.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu EF Core konzentriert sich Dapper hauptsächlich auf das Mapping zwischen SQL-Abfrageergebnissen und .NET-Objekten.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Eigenschaft&lt;br /&gt;
! EF Core&lt;br /&gt;
&lt;br /&gt;
| ! Dapper                                                   |&lt;br /&gt;
| ---------------------------------------------------------- |&lt;br /&gt;
| LINQ-Abfragen                                              |&lt;br /&gt;
| Ja                                                         |&lt;br /&gt;
| Nein beziehungsweise nur eingeschränkt über Zusatzlösungen |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| SQL automatisch erzeugen                                   |&lt;br /&gt;
| Ja                                                         |&lt;br /&gt;
| überwiegend Nein                                           |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| Change Tracking                                            |&lt;br /&gt;
| Ja                                                         |&lt;br /&gt;
| Nein                                                       |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| Migrationen                                                |&lt;br /&gt;
| Ja                                                         |&lt;br /&gt;
| Nein                                                       |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| Beziehungen                                                |&lt;br /&gt;
| umfangreiche Unterstützung                                 |&lt;br /&gt;
| überwiegend manuell                                        |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| Abstraktionsgrad                                           |&lt;br /&gt;
| hoch                                                       |&lt;br /&gt;
| niedrig                                                    |&lt;br /&gt;
| -                                                          |&lt;br /&gt;
| Kontrolle über SQL                                         |&lt;br /&gt;
| mittel bis hoch                                            |&lt;br /&gt;
| sehr hoch                                                  |&lt;br /&gt;
| }                                                          |&lt;br /&gt;
&lt;br /&gt;
Beide Technologien können auch innerhalb derselben Anwendung eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* EF Core für normale CRUD-Funktionen&lt;br /&gt;
* Dapper für wenige hochoptimierte Spezialabfragen&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Entity Framework Core ==&lt;br /&gt;
&lt;br /&gt;
Wichtige Vorteile sind:&lt;br /&gt;
&lt;br /&gt;
* weniger Boilerplate-Code&lt;br /&gt;
* LINQ-Unterstützung&lt;br /&gt;
* starke Typisierung&lt;br /&gt;
* automatische Objektabbildung&lt;br /&gt;
* Change Tracking&lt;br /&gt;
* Migrationen&lt;br /&gt;
* Unterstützung mehrerer Datenbanksysteme&lt;br /&gt;
* gute Integration in ASP.NET Core&lt;br /&gt;
* asynchrone Datenbankoperationen&lt;br /&gt;
* umfangreiche Konfigurationsmöglichkeiten&lt;br /&gt;
* gute Testbarkeit bei geeigneter Architektur&lt;br /&gt;
* automatische Parameterisierung vieler Abfragen&lt;br /&gt;
* produktive Entwicklung komplexer Geschäftsanwendungen&lt;br /&gt;
&lt;br /&gt;
== Nachteile und Risiken ==&lt;br /&gt;
&lt;br /&gt;
EF Core besitzt auch Nachteile.&lt;br /&gt;
&lt;br /&gt;
Dazu gehören:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Abstraktionsschicht&lt;br /&gt;
* generiertes SQL ist nicht immer offensichtlich&lt;br /&gt;
* Entwickler können unbeabsichtigt ineffiziente Queries erzeugen&lt;br /&gt;
* komplexe LINQ-Abfragen können schwer verständlich werden&lt;br /&gt;
* providerabhängiges Verhalten&lt;br /&gt;
* Change Tracking benötigt Ressourcen&lt;br /&gt;
* Migrationen müssen sorgfältig verwaltet werden&lt;br /&gt;
* komplexe Datenbankfeatures können Raw SQL oder providerspezifischen Code erfordern&lt;br /&gt;
&lt;br /&gt;
Ein ORM sollte deshalb nicht als Ersatz für Datenbankwissen betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
== Typische Performancefehler ==&lt;br /&gt;
&lt;br /&gt;
Häufige Probleme sind:&lt;br /&gt;
&lt;br /&gt;
=== Unnötig komplette Tabellen laden ===&lt;br /&gt;
&lt;br /&gt;
Schlecht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und anschließend im Arbeitsspeicher filtern.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ToList zu früh aufrufen ===&lt;br /&gt;
&lt;br /&gt;
Ungünstig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = (await context.Products.ToListAsync())&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden zunächst alle Produkte geladen.&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .Where(p =&amp;gt; p.Price &amp;gt; 100)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tracking bei reinen Leseabfragen ===&lt;br /&gt;
&lt;br /&gt;
Bei großen Read-Only-Abfragen kann:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Zu viele Includes ===&lt;br /&gt;
&lt;br /&gt;
Viele Include()-Aufrufe können sehr große SQL-Abfragen beziehungsweise Ergebnismengen erzeugen.&lt;br /&gt;
&lt;br /&gt;
Eine DTO-Projektion kann effizienter sein.&lt;br /&gt;
&lt;br /&gt;
=== N+1-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Viele kleine Queries können deutlich langsamer sein als wenige gezielt formulierte Queries.&lt;br /&gt;
&lt;br /&gt;
=== Fehlende Indizes ===&lt;br /&gt;
&lt;br /&gt;
Auch eine optimal formulierte EF-Core-Abfrage kann bei fehlenden Datenbankindizes langsam sein.&lt;br /&gt;
&lt;br /&gt;
== Häufige Fehler ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext wurde bereits disposed ===&lt;br /&gt;
&lt;br /&gt;
Typische Ursache:&lt;br /&gt;
&lt;br /&gt;
Der DbContext wird außerhalb seines vorgesehenen Scopes weiterverwendet.&lt;br /&gt;
&lt;br /&gt;
=== A second operation was started on this context ===&lt;br /&gt;
&lt;br /&gt;
Typische Ursache:&lt;br /&gt;
&lt;br /&gt;
Mehrere Datenbankoperationen werden gleichzeitig auf derselben DbContext-Instanz ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Ein DbContext ist nicht für parallele Zugriffe ausgelegt.&lt;br /&gt;
&lt;br /&gt;
=== Entity wird bereits getrackt ===&lt;br /&gt;
&lt;br /&gt;
Typisches Problem:&lt;br /&gt;
&lt;br /&gt;
Zwei Instanzen mit demselben Primärschlüssel sollen gleichzeitig von einem DbContext verfolgt werden.&lt;br /&gt;
&lt;br /&gt;
=== LINQ-Ausdruck kann nicht übersetzt werden ===&lt;br /&gt;
&lt;br /&gt;
Nicht jede beliebige .NET-Methode kann in SQL übersetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise kann eine selbst entwickelte C#-Funktion innerhalb einer Where-Klausel möglicherweise nicht auf dem Datenbankserver ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Migration stimmt nicht mit Datenbank überein ===&lt;br /&gt;
&lt;br /&gt;
Mögliche Ursachen:&lt;br /&gt;
&lt;br /&gt;
* Migration manuell verändert&lt;br /&gt;
* Datenbankschema außerhalb von EF Core geändert&lt;br /&gt;
* Migration nicht ausgeführt&lt;br /&gt;
* falsche Datenbank verwendet&lt;br /&gt;
* unterschiedliche Entwicklungsstände&lt;br /&gt;
&lt;br /&gt;
== Beispiel für eine einfache Anwendung ==&lt;br /&gt;
&lt;br /&gt;
=== Entity ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class Product&lt;br /&gt;
{&lt;br /&gt;
    public int Id { get; set; }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public string Name { get; set; } = string.Empty;&lt;br /&gt;
&lt;br /&gt;
public decimal Price { get; set; }&lt;br /&gt;
&lt;br /&gt;
public decimal Stock { get; set; }&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbContext ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ApplicationDbContext : DbContext&lt;br /&gt;
{&lt;br /&gt;
    public ApplicationDbContext(&lt;br /&gt;
        DbContextOptions&amp;lt;ApplicationDbContext&amp;gt; options)&lt;br /&gt;
        : base(options)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public DbSet&amp;lt;Product&amp;gt; Products =&amp;gt; Set&amp;lt;Product&amp;gt;();&lt;br /&gt;
&lt;br /&gt;
protected override void OnModelCreating(&lt;br /&gt;
    ModelBuilder modelBuilder)&lt;br /&gt;
{&lt;br /&gt;
    modelBuilder.Entity&amp;lt;Product&amp;gt;(entity =&amp;gt;&lt;br /&gt;
    {&lt;br /&gt;
        entity.HasKey(p =&amp;gt; p.Id);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Name)&lt;br /&gt;
            .IsRequired()&lt;br /&gt;
            .HasMaxLength(200);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Price)&lt;br /&gt;
            .HasPrecision(10, 2);&lt;br /&gt;
&lt;br /&gt;
        entity.Property(p =&amp;gt; p.Stock)&lt;br /&gt;
            .HasPrecision(12, 3);&lt;br /&gt;
    });&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Registrierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
builder.Services.AddDbContext&amp;lt;ApplicationDbContext&amp;gt;(&lt;br /&gt;
    options =&amp;gt;&lt;br /&gt;
        options.UseSqlServer(&lt;br /&gt;
            builder.Configuration&lt;br /&gt;
                .GetConnectionString(&amp;quot;DefaultConnection&amp;quot;)));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensatz einfügen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product = new Product&lt;br /&gt;
{&lt;br /&gt;
    Name = &amp;quot;Kalkammonsalpeter&amp;quot;,&lt;br /&gt;
    Price = 289.50m,&lt;br /&gt;
    Stock = 12500&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
context.Products.Add(product);&lt;br /&gt;
&lt;br /&gt;
await context.SaveChangesAsync(); &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensätze lesen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .OrderBy(p =&amp;gt; p.Name)&lt;br /&gt;
    .ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensatz ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product =&lt;br /&gt;
    await context.Products.FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
product.Price = 295.00m;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensatz löschen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var product =&lt;br /&gt;
    await context.Products.FindAsync(productId);&lt;br /&gt;
&lt;br /&gt;
if (product != null)&lt;br /&gt;
{&lt;br /&gt;
context.Products.Remove(product);&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
await context.SaveChangesAsync();&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Empfohlene Projektstruktur ==&lt;br /&gt;
&lt;br /&gt;
Bei einer größeren Anwendung kann beispielsweise folgende Struktur verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Application&lt;br /&gt;
│&lt;br /&gt;
├── Domain&lt;br /&gt;
│   ├── Entities&lt;br /&gt;
│   ├── ValueObjects&lt;br /&gt;
│   └── Enums&lt;br /&gt;
│&lt;br /&gt;
├── Infrastructure&lt;br /&gt;
│   └── Persistence&lt;br /&gt;
│       ├── ApplicationDbContext.cs&lt;br /&gt;
│       ├── Configurations&lt;br /&gt;
│       │   ├── ProductConfiguration.cs&lt;br /&gt;
│       │   ├── CustomerConfiguration.cs&lt;br /&gt;
│       │   └── OrderConfiguration.cs&lt;br /&gt;
│       │&lt;br /&gt;
│       └── Migrations&lt;br /&gt;
│&lt;br /&gt;
├── Application&lt;br /&gt;
│   ├── Services&lt;br /&gt;
│   └── DTOs&lt;br /&gt;
│&lt;br /&gt;
└── Web&lt;br /&gt;
    ├── Controllers&lt;br /&gt;
    ├── Components&lt;br /&gt;
    └── Program.cs&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die konkrete Struktur hängt von Größe und Architektur der Anwendung ab.&lt;br /&gt;
&lt;br /&gt;
Bei kleinen Anwendungen wäre eine solche Aufteilung unter Umständen unnötig komplex.&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== DbContext kurzlebig halten ===&lt;br /&gt;
&lt;br /&gt;
Ein DbContext sollte eine überschaubare Arbeitseinheit repräsentieren.&lt;br /&gt;
&lt;br /&gt;
=== Async verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Serveranwendungen sollten Datenbankzugriffe normalerweise über die asynchronen APIs erfolgen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
await context.Products.ToListAsync();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AsNoTracking bei Read-Only-Abfragen ===&lt;br /&gt;
&lt;br /&gt;
Wenn Entities nicht verändert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
.AsNoTracking()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
verwenden.&lt;br /&gt;
&lt;br /&gt;
=== Projektionen verwenden ===&lt;br /&gt;
&lt;br /&gt;
Nur die tatsächlich benötigten Daten laden.&lt;br /&gt;
&lt;br /&gt;
=== IQueryable nicht unkontrolliert weiterreichen ===&lt;br /&gt;
&lt;br /&gt;
Datenbankabfragen sollten möglichst dort definiert werden, wo deren Auswirkungen nachvollziehbar sind.&lt;br /&gt;
&lt;br /&gt;
=== Generated SQL prüfen ===&lt;br /&gt;
&lt;br /&gt;
Bei komplexen Abfragen sollte das erzeugte SQL kontrolliert werden.&lt;br /&gt;
&lt;br /&gt;
=== Datenbankindizes planen ===&lt;br /&gt;
&lt;br /&gt;
Häufig verwendete Filter-, Join- und Sortierspalten sollten hinsichtlich geeigneter Indizes geprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen versionieren ===&lt;br /&gt;
&lt;br /&gt;
Migrationsdateien gehören normalerweise zusammen mit dem Anwendungscode in die Versionsverwaltung.&lt;br /&gt;
&lt;br /&gt;
=== Migrationen überprüfen ===&lt;br /&gt;
&lt;br /&gt;
Vor dem Deployment sollte geprüft werden, welche Schemaänderungen tatsächlich ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== Connection Strings schützen ===&lt;br /&gt;
&lt;br /&gt;
Passwörter und produktive Zugangsdaten gehören nicht in das Git-Repository.&lt;br /&gt;
&lt;br /&gt;
=== CancellationToken verwenden ===&lt;br /&gt;
&lt;br /&gt;
In Webanwendungen können asynchrone Datenbankoperationen mit CancellationToken ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
var products = await context.Products&lt;br /&gt;
    .AsNoTracking()&lt;br /&gt;
    .ToListAsync(cancellationToken);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Geschäftslogik nicht in den DbContext legen ===&lt;br /&gt;
&lt;br /&gt;
Der DbContext sollte primär für Persistenz und Mapping zuständig sein.&lt;br /&gt;
&lt;br /&gt;
Komplexe Geschäftslogik gehört in geeignete Domain- oder Service-Komponenten.&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core sinnvoll? ==&lt;br /&gt;
&lt;br /&gt;
EF Core eignet sich besonders für:&lt;br /&gt;
&lt;br /&gt;
* ASP.NET-Core-Webanwendungen&lt;br /&gt;
* REST-APIs&lt;br /&gt;
* Blazor-Anwendungen&lt;br /&gt;
* Desktop-Anwendungen mit relationaler Datenbank&lt;br /&gt;
* Worker Services&lt;br /&gt;
* Geschäftsanwendungen&lt;br /&gt;
* CRUD-lastige Systeme&lt;br /&gt;
* Anwendungen mit komplexen Objektbeziehungen&lt;br /&gt;
* Anwendungen, deren Datenbankschema über Migrationen verwaltet werden soll&lt;br /&gt;
&lt;br /&gt;
== Wann ist EF Core möglicherweise nicht die beste Lösung? ==&lt;br /&gt;
&lt;br /&gt;
Andere Lösungen können sinnvoller sein, wenn:&lt;br /&gt;
&lt;br /&gt;
* ausschließlich wenige, sehr einfache SQL-Abfragen erforderlich sind&lt;br /&gt;
* vollständige Kontrolle über jedes SQL-Statement notwendig ist&lt;br /&gt;
* extrem performancekritische Spezialabfragen dominieren&lt;br /&gt;
* eine sehr ungewöhnliche Legacy-Datenbank angebunden wird&lt;br /&gt;
* Stored Procedures die gesamte Datenzugriffsschicht bestimmen&lt;br /&gt;
* kein objektorientiertes Datenmodell benötigt wird&lt;br /&gt;
&lt;br /&gt;
Auch eine Kombination mehrerer Technologien ist möglich.&lt;br /&gt;
&lt;br /&gt;
== EF Core in Blazor ==&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;Blazor Server&#039;&#039;&#039; beziehungsweise interaktiven serverseitigen Blazor-Anwendungen muss besonders auf die Lebensdauer des DbContext geachtet werden.&lt;br /&gt;
&lt;br /&gt;
Ein normaler ASP.NET-Core-HTTP-Request ist kurzlebig. Ein Blazor-Circuit kann dagegen über einen wesentlich längeren Zeitraum bestehen.&lt;br /&gt;
&lt;br /&gt;
Aus diesem Grund ist es häufig sinnvoll, pro Datenbankoperation beziehungsweise pro Unit of Work einen eigenen DbContext über:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;csharp&amp;quot;&amp;gt;&lt;br /&gt;
public class ProductService&lt;br /&gt;
{&lt;br /&gt;
    private readonly IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt;&lt;br /&gt;
        _contextFactory;&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
public ProductService(&lt;br /&gt;
    IDbContextFactory&amp;lt;ApplicationDbContext&amp;gt; contextFactory)&lt;br /&gt;
{&lt;br /&gt;
    _contextFactory = contextFactory;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public async Task&amp;lt;List&amp;lt;Product&amp;gt;&amp;gt; GetProductsAsync()&lt;br /&gt;
{&lt;br /&gt;
    await using var context =&lt;br /&gt;
        await _contextFactory.CreateDbContextAsync();&lt;br /&gt;
&lt;br /&gt;
    return await context.Products&lt;br /&gt;
        .AsNoTracking()&lt;br /&gt;
        .ToListAsync();&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
} &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies reduziert unter anderem Probleme mit:&lt;br /&gt;
&lt;br /&gt;
* langen Tracking-Zeiträumen&lt;br /&gt;
* parallelen Operationen&lt;br /&gt;
* veralteten Entity-Zuständen&lt;br /&gt;
* unnötigem Speicherverbrauch&lt;br /&gt;
&lt;br /&gt;
== EF-Core-Versionen ==&lt;br /&gt;
&lt;br /&gt;
EF Core folgt weitgehend dem Veröffentlichungszyklus von .NET.&lt;br /&gt;
&lt;br /&gt;
Aktuell relevante Generationen sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! EF-Core-Version&lt;br /&gt;
! Zielplattform&lt;br /&gt;
&lt;br /&gt;
| ! Bemerkung                       |&lt;br /&gt;
| --------------------------------- |&lt;br /&gt;
| EF Core 10                        |&lt;br /&gt;
| .NET 10                           |&lt;br /&gt;
| aktuelle LTS-Generation           |&lt;br /&gt;
| -                                 |&lt;br /&gt;
| EF Core 9                         |&lt;br /&gt;
| .NET 8                            |&lt;br /&gt;
| weiterhin unterstützte Generation |&lt;br /&gt;
| -                                 |&lt;br /&gt;
| EF Core 8                         |&lt;br /&gt;
| .NET 8                            |&lt;br /&gt;
| LTS-Generation                    |&lt;br /&gt;
|}                                 |&lt;br /&gt;
&lt;br /&gt;
Bei neuen Projekten sollte grundsätzlich geprüft werden, welche .NET- und EF-Core-Version aktuell unterstützt wird.&lt;br /&gt;
&lt;br /&gt;
Bei einem Major-Upgrade, beispielsweise von EF Core 9 auf EF Core 10, sollten außerdem die dokumentierten &#039;&#039;&#039;Breaking Changes&#039;&#039;&#039; geprüft werden.&lt;br /&gt;
&lt;br /&gt;
Die Versionen der verwendeten EF-Core-Pakete sollten untereinander möglichst übereinstimmen.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Microsoft.EntityFrameworkCore                 10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.SqlServer       10.x&lt;br /&gt;
Microsoft.EntityFrameworkCore.Design          10.x&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entity Framework Core&#039;&#039;&#039; ist die Standard-ORM-Technologie für viele moderne .NET-Anwendungen.&lt;br /&gt;
&lt;br /&gt;
Das zentrale Modell besteht aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Entity&lt;br /&gt;
   ↓&lt;br /&gt;
DbSet&lt;br /&gt;
   ↓&lt;br /&gt;
DbContext&lt;br /&gt;
   ↓&lt;br /&gt;
EF Core Provider&lt;br /&gt;
   ↓&lt;br /&gt;
Datenbank&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* Entities repräsentieren Datenbankdatensätze.&lt;br /&gt;
* DbSet repräsentiert eine Menge von Entities.&lt;br /&gt;
* DbContext verwaltet Datenbankzugriffe und Änderungen.&lt;br /&gt;
* LINQ wird zur Formulierung von Abfragen verwendet.&lt;br /&gt;
* Change Tracking erkennt Änderungen an Entities.&lt;br /&gt;
* SaveChanges überträgt Änderungen zur Datenbank.&lt;br /&gt;
* Migrationen verwalten Änderungen am Datenbankschema.&lt;br /&gt;
* Beziehungen können als Navigation Properties modelliert werden.&lt;br /&gt;
* Datenbankprovider ermöglichen die Unterstützung verschiedener Datenbanksysteme.&lt;br /&gt;
* AsNoTracking und Projektionen können Read-Only-Abfragen effizienter machen.&lt;br /&gt;
* DbContext-Instanzen sollten kurzlebig und nicht parallel verwendet werden.&lt;br /&gt;
* Trotz ORM bleiben Kenntnisse über SQL und relationale Datenbanken wichtig.&lt;br /&gt;
&lt;br /&gt;
Für typische Geschäftsanwendungen bietet EF Core einen guten Kompromiss zwischen &#039;&#039;&#039;Entwicklungsgeschwindigkeit&#039;&#039;&#039;, &#039;&#039;&#039;Typsicherheit&#039;&#039;&#039;, &#039;&#039;&#039;Wartbarkeit&#039;&#039;&#039; und &#039;&#039;&#039;Kontrolle über den Datenbankzugriff&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[.NET]]&lt;br /&gt;
* [[ASP.NET Core]]&lt;br /&gt;
* [[Blazor]]&lt;br /&gt;
* [[C Sharp|C#]]&lt;br /&gt;
* [[LINQ]]&lt;br /&gt;
* [[SQL]]&lt;br /&gt;
* [[Object-Relational Mapping]]&lt;br /&gt;
* [[Dependency Injection]]&lt;br /&gt;
* [[Repository Pattern]]&lt;br /&gt;
* [[Unit of Work]]&lt;br /&gt;
* [[SQL Server]]&lt;br /&gt;
* [[SQLite]]&lt;br /&gt;
* [[PostgreSQL]]&lt;br /&gt;
* [[MariaDB]]&lt;br /&gt;
* [[Docker]]&lt;br /&gt;
* [[CI/CD]]&lt;br /&gt;
&lt;br /&gt;
== Quellen und weiterführende Dokumentation ==&lt;br /&gt;
&lt;br /&gt;
* Microsoft Learn: Entity Framework Core – Dokumentation&lt;br /&gt;
* Microsoft Learn: EF Core – Erste Schritte&lt;br /&gt;
* Microsoft Learn: EF Core – Change Tracking&lt;br /&gt;
* Microsoft Learn: EF Core – Beziehungen&lt;br /&gt;
* Microsoft Learn: EF Core – Migrationen&lt;br /&gt;
* Microsoft Learn: EF Core – Performance&lt;br /&gt;
* Microsoft Learn: EF Core – Releases und Planung&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:C Sharp]]&lt;br /&gt;
[[Kategorie:Datenbank]]&lt;br /&gt;
[[Kategorie:Entity Framework]]&lt;br /&gt;
[[Kategorie:Object-Relational Mapping]]&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=209</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=209"/>
		<updated>2026-09-09T08:42:06Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* C# */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Software-Entwicklung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[Entwicklung mit Git, Visual Studio und Codex]]&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
=== dot.net ===&lt;br /&gt;
* [[Entity Framework Core (EF Core)]]&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[Lineare Gewichtung]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== KI und Machine Learning==&lt;br /&gt;
* [[Beam Search]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=208</id>
		<title>Entwicklung mit Git, Visual Studio und Codex</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=208"/>
		<updated>2026-09-01T06:19:26Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Git-Funktionen in Visual Studio */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Git, Visual Studio und Codex =&lt;br /&gt;
&lt;br /&gt;
Diese Seite beschreibt die wichtigsten Git-Funktionen in Visual Studio sowie den empfohlenen Workflow bei der parallelen Verwendung von Visual Studio und Codex für .NET-Projekte.&lt;br /&gt;
&lt;br /&gt;
== Git-Funktionen in Visual Studio ==&lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Git-Änderungen&#039;&#039;&#039; stellt Visual Studio verschiedene Funktionen zur Synchronisation mit einem Remote-Repository wie GitHub bereit.&lt;br /&gt;
&lt;br /&gt;
=== Commit ===&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Commit-Funktion&#039;&#039;&#039; in Visual Studio dient dazu, Änderungen an einem Projekt dauerhaft in der lokalen &#039;&#039;&#039;Git-Versionshistorie&#039;&#039;&#039; zu speichern. Ein Commit stellt dabei einen bestimmten Entwicklungsstand des Projekts dar, zu dem später zurückgekehrt werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Typischer Ablauf ====&lt;br /&gt;
&lt;br /&gt;
# Quellcode oder andere Projektdateien ändern.&lt;br /&gt;
# Das Fenster &#039;&#039;&#039;Git-Änderungen&#039;&#039;&#039; öffnen.&lt;br /&gt;
# Die vorgenommenen Änderungen kontrollieren.&lt;br /&gt;
# Eine aussagekräftige &#039;&#039;&#039;Commit-Nachricht&#039;&#039;&#039; eingeben, z. B.:&lt;br /&gt;
#: &amp;lt;code&amp;gt;Fehler bei der seriellen Kommunikation behoben&amp;lt;/code&amp;gt;&lt;br /&gt;
# Auf &#039;&#039;&#039;Alle committen&#039;&#039;&#039; klicken.&lt;br /&gt;
# Bei Verwendung eines entfernten Repositorys, beispielsweise GitHub, anschließend die Commits mit &#039;&#039;&#039;Push&#039;&#039;&#039; übertragen.&lt;br /&gt;
&lt;br /&gt;
Der grundlegende Ablauf lautet damit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Dateien ändern&lt;br /&gt;
     ↓&lt;br /&gt;
Commit&lt;br /&gt;
     ↓&lt;br /&gt;
Lokales Git-Repository&lt;br /&gt;
     ↓&lt;br /&gt;
Push&lt;br /&gt;
     ↓&lt;br /&gt;
GitHub / Remote Repository&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Commit und Push ====&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Commit&#039;&#039;&#039; speichert Änderungen zunächst nur im lokalen Git-Repository auf dem eigenen Computer. Die Änderungen befinden sich dadurch noch nicht automatisch auf GitHub.&lt;br /&gt;
&lt;br /&gt;
Erst ein anschließender &#039;&#039;&#039;Push&#039;&#039;&#039; überträgt die lokalen Commits in das entfernte Repository.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Commit&#039;&#039;&#039;&lt;br /&gt;
| Speichert einen Entwicklungsstand im lokalen Git-Repository.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Push&#039;&#039;&#039;&lt;br /&gt;
| Überträgt lokale Commits zu einem entfernten Repository, beispielsweise GitHub.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Alle committen ====&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;Alle committen&#039;&#039;&#039; werden alle aktuell vorhandenen Änderungen in einem neuen Commit gespeichert. Voraussetzung ist, dass Änderungen vorhanden sind und eine Commit-Nachricht eingegeben wurde.&lt;br /&gt;
&lt;br /&gt;
Sind keine Änderungen vorhanden, ist die Schaltfläche entsprechend deaktiviert.&lt;br /&gt;
&lt;br /&gt;
==== Staging ====&lt;br /&gt;
&lt;br /&gt;
Git bietet zusätzlich einen sogenannten &#039;&#039;&#039;Staging-Bereich&#039;&#039;&#039;. Damit kann festgelegt werden, welche Änderungen in den nächsten Commit aufgenommen werden sollen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
MainForm.cs&lt;br /&gt;
SerialPort.cs&lt;br /&gt;
README.md&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wurden alle drei Dateien geändert, sollen aber nur &amp;lt;code&amp;gt;MainForm.cs&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;SerialPort.cs&amp;lt;/code&amp;gt; zusammen committen werden, können diese beiden Dateien zunächst &#039;&#039;&#039;gestaged&#039;&#039;&#039; und anschließend separat committet werden.&lt;br /&gt;
&lt;br /&gt;
==== Commit korrigieren (Amend) ====&lt;br /&gt;
&lt;br /&gt;
Die Option &#039;&#039;&#039;Korrigieren&#039;&#039;&#039; entspricht der Git-Funktion &#039;&#039;&#039;Amend&#039;&#039;&#039;. Damit wird kein zusätzlicher Commit erstellt, sondern der zuletzt erstellte Commit verändert.&lt;br /&gt;
&lt;br /&gt;
Dies ist beispielsweise nützlich, wenn unmittelbar nach einem Commit festgestellt wird, dass eine Datei vergessen oder die Commit-Nachricht falsch formuliert wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Vorher:&lt;br /&gt;
A → B → C&lt;br /&gt;
&lt;br /&gt;
Nach Amend:&lt;br /&gt;
A → B → C&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Amend sollte möglichst nicht für Commits verwendet werden, die bereits zu GitHub gepusht und mit anderen Entwicklern geteilt wurden, da hierbei die Commit-Historie verändert wird.&lt;br /&gt;
&lt;br /&gt;
==== Gute Commit-Nachrichten ====&lt;br /&gt;
&lt;br /&gt;
Commit-Nachrichten sollten möglichst kurz beschreiben, was mit dem Commit geändert wurde.&lt;br /&gt;
&lt;br /&gt;
Gute Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Implementiere Modbus-TCP-Verbindung zum Terminal&lt;br /&gt;
Behebe Fehler beim Parsen der Gewichtsdaten&lt;br /&gt;
Füge Validierung der Benutzereingaben hinzu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenig aussagekräftig sind dagegen Nachrichten wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Update&lt;br /&gt;
Änderungen&lt;br /&gt;
Test&lt;br /&gt;
Neu&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039; Ein &#039;&#039;&#039;Commit&#039;&#039;&#039; ist ein Speicherpunkt in der lokalen Git-Historie. Ein &#039;&#039;&#039;Push&#039;&#039;&#039; überträgt diese Speicherpunkte anschließend zu einem entfernten Repository wie GitHub.&lt;br /&gt;
&lt;br /&gt;
=== Fetch ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fetch&#039;&#039;&#039; lädt Informationen über neue Commits und Branches vom Remote-Repository herunter, verändert den lokalen Arbeitsstand jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fetch eignet sich, wenn zunächst geprüft werden soll, ob auf dem Remote-Repository neue Änderungen vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Fetch = Änderungen vom Server abrufen, aber noch nicht in den eigenen Branch übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Pull ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pull&#039;&#039;&#039; lädt Änderungen vom Remote-Repository herunter und integriert sie in den aktuell ausgecheckten lokalen Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht entspricht Pull:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fetch&lt;br /&gt;
  +&lt;br /&gt;
Merge bzw. Rebase&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei können Merge-Konflikte entstehen, wenn lokale und entfernte Änderungen dieselben Bereiche einer Datei betreffen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Pull = Änderungen herunterladen und in den eigenen Arbeitsstand übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Push ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Push&#039;&#039;&#039; überträgt lokal erstellte Commits zum Remote-Repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git push&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nur Änderungen übertragen, die zuvor als Commit gespeichert wurden.&lt;br /&gt;
&lt;br /&gt;
Befinden sich auf dem Remote-Repository bereits neuere Commits, die lokal noch nicht vorhanden sind, kann Git den Push ablehnen. In diesem Fall sollte normalerweise zunächst ein Pull durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Push = Eigene Commits zum Server hochladen.&lt;br /&gt;
&lt;br /&gt;
=== Sync ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sync&#039;&#039;&#039; synchronisiert den lokalen und den entfernten Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht führt Visual Studio dabei folgende Schritte aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zuerst werden also Änderungen vom Remote-Repository übernommen. Anschließend werden die eigenen lokalen Commits hochgeladen.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Optionen ===&lt;br /&gt;
&lt;br /&gt;
Über das Symbol&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
stehen zusätzliche Git-Funktionen und Synchronisationsoptionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
== Typischer Git-Workflow ==&lt;br /&gt;
&lt;br /&gt;
Ein normaler Arbeitsablauf kann beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Code bearbeiten&lt;br /&gt;
 ↓&lt;br /&gt;
Änderungen testen&lt;br /&gt;
 ↓&lt;br /&gt;
Commit erstellen&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional kann vorher mit &#039;&#039;&#039;Fetch&#039;&#039;&#039; geprüft werden, ob neue Änderungen im Remote-Repository vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
== Visual Studio und Codex gleichzeitig verwenden ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können parallel mit demselben .NET-Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio nach Änderungen durch Codex ist normalerweise &#039;&#039;&#039;nicht erforderlich&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Codex verändert die Dateien direkt im Projektverzeichnis. Visual Studio erkennt externe Dateiänderungen in der Regel automatisch.&lt;br /&gt;
&lt;br /&gt;
Typische Dateien sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.cs&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.razor&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.xaml&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesen Dateien ist normalerweise kein erneutes Laden des Projekts notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen an Projektdateien ===&lt;br /&gt;
&lt;br /&gt;
Bei Änderungen an Dateien wie&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.csproj&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.sln&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann Visual Studio gegebenenfalls verlangen, das Projekt neu zu laden.&lt;br /&gt;
&lt;br /&gt;
Ein kompletter Neustart von Visual Studio ist normalerweise trotzdem nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Laufende Anwendung und Hot Reload ==&lt;br /&gt;
&lt;br /&gt;
Dass Visual Studio eine geänderte Datei erkennt, bedeutet nicht automatisch, dass die Änderung sofort in einer bereits laufenden Anwendung aktiv wird.&lt;br /&gt;
&lt;br /&gt;
Je nach Änderung kann &#039;&#039;&#039;Hot Reload&#039;&#039;&#039; die Änderung während der Laufzeit übernehmen.&lt;br /&gt;
&lt;br /&gt;
Ist die Änderung nicht mit Hot Reload kompatibel, reicht normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Anwendung stoppen&lt;br /&gt;
 ↓&lt;br /&gt;
Projekt neu kompilieren&lt;br /&gt;
 ↓&lt;br /&gt;
Anwendung erneut starten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio selbst muss dafür nicht neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Erkennt Codex Änderungen aus Visual Studio? ==&lt;br /&gt;
&lt;br /&gt;
Ja.&lt;br /&gt;
&lt;br /&gt;
Wenn Visual Studio und Codex auf demselben Projektverzeichnis arbeiten, kann Codex Änderungen sehen, die zuvor in Visual Studio durchgeführt wurden.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist normalerweise, dass die Datei &#039;&#039;&#039;gespeichert&#039;&#039;&#039; wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio&lt;br /&gt;
     ↓&lt;br /&gt;
Datei speichern&lt;br /&gt;
     ↓&lt;br /&gt;
Datei auf der Festplatte&lt;br /&gt;
     ↓&lt;br /&gt;
Codex liest die aktuelle Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Eine Datei&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
ProjectService.cs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird in Visual Studio geändert und gespeichert.&lt;br /&gt;
&lt;br /&gt;
Wenn Codex anschließend diese Datei bearbeitet, verwendet Codex den aktuellen gespeicherten Stand.&lt;br /&gt;
&lt;br /&gt;
=== Ungespeicherte Änderungen ===&lt;br /&gt;
&lt;br /&gt;
Ungespeicherte Änderungen befinden sich nur im Editor von Visual Studio.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio:&lt;br /&gt;
neue Änderung&lt;br /&gt;
     ↓&lt;br /&gt;
nicht gespeichert&lt;br /&gt;
&lt;br /&gt;
Festplatte:&lt;br /&gt;
alte Version&lt;br /&gt;
&lt;br /&gt;
Codex:&lt;br /&gt;
sieht die alte Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollten Änderungen in Visual Studio gespeichert werden, bevor Codex mit der Bearbeitung beginnt.&lt;br /&gt;
&lt;br /&gt;
== Gleichzeitiges Bearbeiten derselben Datei ==&lt;br /&gt;
&lt;br /&gt;
Problematisch kann es werden, wenn Visual Studio und Codex gleichzeitig dieselbe Datei bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio                 Codex&lt;br /&gt;
     │                          │&lt;br /&gt;
ändert MainPage.xaml     ändert MainPage.xaml&lt;br /&gt;
     │                          │&lt;br /&gt;
nicht gespeichert         speichert Änderung&lt;br /&gt;
     │                          │&lt;br /&gt;
     └──── Konflikt möglich ────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio kann anschließend melden, dass die Datei außerhalb der Entwicklungsumgebung geändert wurde.&lt;br /&gt;
&lt;br /&gt;
Wird danach die noch ungespeicherte Version aus Visual Studio gespeichert, können die Änderungen von Codex überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Empfohlener Workflow mit Codex ==&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio durchführen&lt;br /&gt;
# Alle Dateien speichern&lt;br /&gt;
# Codex einen Auftrag geben&lt;br /&gt;
# Codex die Änderungen durchführen lassen&lt;br /&gt;
# Änderungen in Visual Studio kontrollieren&lt;br /&gt;
# Projekt kompilieren&lt;br /&gt;
# Anwendung testen&lt;br /&gt;
# Git-Diff überprüfen&lt;br /&gt;
# Commit erstellen&lt;br /&gt;
# Änderungen mit Push zum Remote-Repository übertragen&lt;br /&gt;
&lt;br /&gt;
== Empfehlung ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können problemlos parallel auf demselben Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Dabei sollte folgende Regel beachtet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Visual Studio und Codex sollten möglichst nicht gleichzeitig dieselbe Datei bearbeiten.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vor der Arbeit mit Codex sollten alle Änderungen in Visual Studio gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio oder Codex ist im normalen Entwicklungsworkflow nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Kurzreferenz ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktion !! Bedeutung !! Lokale Dateien werden verändert?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fetch&#039;&#039;&#039;                                                  &lt;br /&gt;
| Neue Informationen und Commits vom Remote-Repository abrufen &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Pull&#039;&#039;&#039;                                                   &lt;br /&gt;
| Remote-Änderungen herunterladen und übernehmen               &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Push&#039;&#039;&#039;                                                   &lt;br /&gt;
| Eigene Commits zum Remote-Repository übertragen              &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Sync&#039;&#039;&#039;                                                   &lt;br /&gt;
| Pull und anschließend Push durchführen                       &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|}                                                            &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Git]]&lt;br /&gt;
[[Kategorie:Visual Studio]]&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:Codex]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=207</id>
		<title>Entwicklung mit Git, Visual Studio und Codex</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=207"/>
		<updated>2026-08-19T11:48:43Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Empfohlener Workflow mit Codex */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Git, Visual Studio und Codex =&lt;br /&gt;
&lt;br /&gt;
Diese Seite beschreibt die wichtigsten Git-Funktionen in Visual Studio sowie den empfohlenen Workflow bei der parallelen Verwendung von Visual Studio und Codex für .NET-Projekte.&lt;br /&gt;
&lt;br /&gt;
== Git-Funktionen in Visual Studio ==&lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Git-Änderungen&#039;&#039;&#039; stellt Visual Studio verschiedene Funktionen zur Synchronisation mit einem Remote-Repository wie GitHub bereit.&lt;br /&gt;
&lt;br /&gt;
=== Fetch ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fetch&#039;&#039;&#039; lädt Informationen über neue Commits und Branches vom Remote-Repository herunter, verändert den lokalen Arbeitsstand jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fetch eignet sich, wenn zunächst geprüft werden soll, ob auf dem Remote-Repository neue Änderungen vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Fetch = Änderungen vom Server abrufen, aber noch nicht in den eigenen Branch übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Pull ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pull&#039;&#039;&#039; lädt Änderungen vom Remote-Repository herunter und integriert sie in den aktuell ausgecheckten lokalen Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht entspricht Pull:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fetch&lt;br /&gt;
  +&lt;br /&gt;
Merge bzw. Rebase&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei können Merge-Konflikte entstehen, wenn lokale und entfernte Änderungen dieselben Bereiche einer Datei betreffen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Pull = Änderungen herunterladen und in den eigenen Arbeitsstand übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Push ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Push&#039;&#039;&#039; überträgt lokal erstellte Commits zum Remote-Repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git push&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nur Änderungen übertragen, die zuvor als Commit gespeichert wurden.&lt;br /&gt;
&lt;br /&gt;
Befinden sich auf dem Remote-Repository bereits neuere Commits, die lokal noch nicht vorhanden sind, kann Git den Push ablehnen. In diesem Fall sollte normalerweise zunächst ein Pull durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Push = Eigene Commits zum Server hochladen.&lt;br /&gt;
&lt;br /&gt;
=== Sync ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sync&#039;&#039;&#039; synchronisiert den lokalen und den entfernten Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht führt Visual Studio dabei folgende Schritte aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zuerst werden also Änderungen vom Remote-Repository übernommen. Anschließend werden die eigenen lokalen Commits hochgeladen.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Optionen ===&lt;br /&gt;
&lt;br /&gt;
Über das Symbol&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
stehen zusätzliche Git-Funktionen und Synchronisationsoptionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
== Typischer Git-Workflow ==&lt;br /&gt;
&lt;br /&gt;
Ein normaler Arbeitsablauf kann beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Code bearbeiten&lt;br /&gt;
 ↓&lt;br /&gt;
Änderungen testen&lt;br /&gt;
 ↓&lt;br /&gt;
Commit erstellen&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional kann vorher mit &#039;&#039;&#039;Fetch&#039;&#039;&#039; geprüft werden, ob neue Änderungen im Remote-Repository vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
== Visual Studio und Codex gleichzeitig verwenden ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können parallel mit demselben .NET-Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio nach Änderungen durch Codex ist normalerweise &#039;&#039;&#039;nicht erforderlich&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Codex verändert die Dateien direkt im Projektverzeichnis. Visual Studio erkennt externe Dateiänderungen in der Regel automatisch.&lt;br /&gt;
&lt;br /&gt;
Typische Dateien sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.cs&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.razor&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.xaml&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesen Dateien ist normalerweise kein erneutes Laden des Projekts notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen an Projektdateien ===&lt;br /&gt;
&lt;br /&gt;
Bei Änderungen an Dateien wie&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.csproj&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.sln&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann Visual Studio gegebenenfalls verlangen, das Projekt neu zu laden.&lt;br /&gt;
&lt;br /&gt;
Ein kompletter Neustart von Visual Studio ist normalerweise trotzdem nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Laufende Anwendung und Hot Reload ==&lt;br /&gt;
&lt;br /&gt;
Dass Visual Studio eine geänderte Datei erkennt, bedeutet nicht automatisch, dass die Änderung sofort in einer bereits laufenden Anwendung aktiv wird.&lt;br /&gt;
&lt;br /&gt;
Je nach Änderung kann &#039;&#039;&#039;Hot Reload&#039;&#039;&#039; die Änderung während der Laufzeit übernehmen.&lt;br /&gt;
&lt;br /&gt;
Ist die Änderung nicht mit Hot Reload kompatibel, reicht normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Anwendung stoppen&lt;br /&gt;
 ↓&lt;br /&gt;
Projekt neu kompilieren&lt;br /&gt;
 ↓&lt;br /&gt;
Anwendung erneut starten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio selbst muss dafür nicht neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Erkennt Codex Änderungen aus Visual Studio? ==&lt;br /&gt;
&lt;br /&gt;
Ja.&lt;br /&gt;
&lt;br /&gt;
Wenn Visual Studio und Codex auf demselben Projektverzeichnis arbeiten, kann Codex Änderungen sehen, die zuvor in Visual Studio durchgeführt wurden.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist normalerweise, dass die Datei &#039;&#039;&#039;gespeichert&#039;&#039;&#039; wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio&lt;br /&gt;
     ↓&lt;br /&gt;
Datei speichern&lt;br /&gt;
     ↓&lt;br /&gt;
Datei auf der Festplatte&lt;br /&gt;
     ↓&lt;br /&gt;
Codex liest die aktuelle Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Eine Datei&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
ProjectService.cs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird in Visual Studio geändert und gespeichert.&lt;br /&gt;
&lt;br /&gt;
Wenn Codex anschließend diese Datei bearbeitet, verwendet Codex den aktuellen gespeicherten Stand.&lt;br /&gt;
&lt;br /&gt;
=== Ungespeicherte Änderungen ===&lt;br /&gt;
&lt;br /&gt;
Ungespeicherte Änderungen befinden sich nur im Editor von Visual Studio.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio:&lt;br /&gt;
neue Änderung&lt;br /&gt;
     ↓&lt;br /&gt;
nicht gespeichert&lt;br /&gt;
&lt;br /&gt;
Festplatte:&lt;br /&gt;
alte Version&lt;br /&gt;
&lt;br /&gt;
Codex:&lt;br /&gt;
sieht die alte Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollten Änderungen in Visual Studio gespeichert werden, bevor Codex mit der Bearbeitung beginnt.&lt;br /&gt;
&lt;br /&gt;
== Gleichzeitiges Bearbeiten derselben Datei ==&lt;br /&gt;
&lt;br /&gt;
Problematisch kann es werden, wenn Visual Studio und Codex gleichzeitig dieselbe Datei bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio                 Codex&lt;br /&gt;
     │                          │&lt;br /&gt;
ändert MainPage.xaml     ändert MainPage.xaml&lt;br /&gt;
     │                          │&lt;br /&gt;
nicht gespeichert         speichert Änderung&lt;br /&gt;
     │                          │&lt;br /&gt;
     └──── Konflikt möglich ────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio kann anschließend melden, dass die Datei außerhalb der Entwicklungsumgebung geändert wurde.&lt;br /&gt;
&lt;br /&gt;
Wird danach die noch ungespeicherte Version aus Visual Studio gespeichert, können die Änderungen von Codex überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Empfohlener Workflow mit Codex ==&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio durchführen&lt;br /&gt;
# Alle Dateien speichern&lt;br /&gt;
# Codex einen Auftrag geben&lt;br /&gt;
# Codex die Änderungen durchführen lassen&lt;br /&gt;
# Änderungen in Visual Studio kontrollieren&lt;br /&gt;
# Projekt kompilieren&lt;br /&gt;
# Anwendung testen&lt;br /&gt;
# Git-Diff überprüfen&lt;br /&gt;
# Commit erstellen&lt;br /&gt;
# Änderungen mit Push zum Remote-Repository übertragen&lt;br /&gt;
&lt;br /&gt;
== Empfehlung ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können problemlos parallel auf demselben Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Dabei sollte folgende Regel beachtet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Visual Studio und Codex sollten möglichst nicht gleichzeitig dieselbe Datei bearbeiten.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vor der Arbeit mit Codex sollten alle Änderungen in Visual Studio gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio oder Codex ist im normalen Entwicklungsworkflow nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Kurzreferenz ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktion !! Bedeutung !! Lokale Dateien werden verändert?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fetch&#039;&#039;&#039;                                                  &lt;br /&gt;
| Neue Informationen und Commits vom Remote-Repository abrufen &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Pull&#039;&#039;&#039;                                                   &lt;br /&gt;
| Remote-Änderungen herunterladen und übernehmen               &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Push&#039;&#039;&#039;                                                   &lt;br /&gt;
| Eigene Commits zum Remote-Repository übertragen              &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Sync&#039;&#039;&#039;                                                   &lt;br /&gt;
| Pull und anschließend Push durchführen                       &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|}                                                            &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Git]]&lt;br /&gt;
[[Kategorie:Visual Studio]]&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:Codex]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=206</id>
		<title>Entwicklung mit Git, Visual Studio und Codex</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=206"/>
		<updated>2026-08-19T11:48:23Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Kurzreferenz */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Git, Visual Studio und Codex =&lt;br /&gt;
&lt;br /&gt;
Diese Seite beschreibt die wichtigsten Git-Funktionen in Visual Studio sowie den empfohlenen Workflow bei der parallelen Verwendung von Visual Studio und Codex für .NET-Projekte.&lt;br /&gt;
&lt;br /&gt;
== Git-Funktionen in Visual Studio ==&lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Git-Änderungen&#039;&#039;&#039; stellt Visual Studio verschiedene Funktionen zur Synchronisation mit einem Remote-Repository wie GitHub bereit.&lt;br /&gt;
&lt;br /&gt;
=== Fetch ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fetch&#039;&#039;&#039; lädt Informationen über neue Commits und Branches vom Remote-Repository herunter, verändert den lokalen Arbeitsstand jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fetch eignet sich, wenn zunächst geprüft werden soll, ob auf dem Remote-Repository neue Änderungen vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Fetch = Änderungen vom Server abrufen, aber noch nicht in den eigenen Branch übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Pull ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pull&#039;&#039;&#039; lädt Änderungen vom Remote-Repository herunter und integriert sie in den aktuell ausgecheckten lokalen Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht entspricht Pull:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fetch&lt;br /&gt;
  +&lt;br /&gt;
Merge bzw. Rebase&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei können Merge-Konflikte entstehen, wenn lokale und entfernte Änderungen dieselben Bereiche einer Datei betreffen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Pull = Änderungen herunterladen und in den eigenen Arbeitsstand übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Push ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Push&#039;&#039;&#039; überträgt lokal erstellte Commits zum Remote-Repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git push&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nur Änderungen übertragen, die zuvor als Commit gespeichert wurden.&lt;br /&gt;
&lt;br /&gt;
Befinden sich auf dem Remote-Repository bereits neuere Commits, die lokal noch nicht vorhanden sind, kann Git den Push ablehnen. In diesem Fall sollte normalerweise zunächst ein Pull durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Push = Eigene Commits zum Server hochladen.&lt;br /&gt;
&lt;br /&gt;
=== Sync ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sync&#039;&#039;&#039; synchronisiert den lokalen und den entfernten Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht führt Visual Studio dabei folgende Schritte aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zuerst werden also Änderungen vom Remote-Repository übernommen. Anschließend werden die eigenen lokalen Commits hochgeladen.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Optionen ===&lt;br /&gt;
&lt;br /&gt;
Über das Symbol&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
stehen zusätzliche Git-Funktionen und Synchronisationsoptionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
== Typischer Git-Workflow ==&lt;br /&gt;
&lt;br /&gt;
Ein normaler Arbeitsablauf kann beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Code bearbeiten&lt;br /&gt;
 ↓&lt;br /&gt;
Änderungen testen&lt;br /&gt;
 ↓&lt;br /&gt;
Commit erstellen&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional kann vorher mit &#039;&#039;&#039;Fetch&#039;&#039;&#039; geprüft werden, ob neue Änderungen im Remote-Repository vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
== Visual Studio und Codex gleichzeitig verwenden ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können parallel mit demselben .NET-Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio nach Änderungen durch Codex ist normalerweise &#039;&#039;&#039;nicht erforderlich&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Codex verändert die Dateien direkt im Projektverzeichnis. Visual Studio erkennt externe Dateiänderungen in der Regel automatisch.&lt;br /&gt;
&lt;br /&gt;
Typische Dateien sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.cs&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.razor&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.xaml&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesen Dateien ist normalerweise kein erneutes Laden des Projekts notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen an Projektdateien ===&lt;br /&gt;
&lt;br /&gt;
Bei Änderungen an Dateien wie&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.csproj&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.sln&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann Visual Studio gegebenenfalls verlangen, das Projekt neu zu laden.&lt;br /&gt;
&lt;br /&gt;
Ein kompletter Neustart von Visual Studio ist normalerweise trotzdem nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Laufende Anwendung und Hot Reload ==&lt;br /&gt;
&lt;br /&gt;
Dass Visual Studio eine geänderte Datei erkennt, bedeutet nicht automatisch, dass die Änderung sofort in einer bereits laufenden Anwendung aktiv wird.&lt;br /&gt;
&lt;br /&gt;
Je nach Änderung kann &#039;&#039;&#039;Hot Reload&#039;&#039;&#039; die Änderung während der Laufzeit übernehmen.&lt;br /&gt;
&lt;br /&gt;
Ist die Änderung nicht mit Hot Reload kompatibel, reicht normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Anwendung stoppen&lt;br /&gt;
 ↓&lt;br /&gt;
Projekt neu kompilieren&lt;br /&gt;
 ↓&lt;br /&gt;
Anwendung erneut starten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio selbst muss dafür nicht neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Erkennt Codex Änderungen aus Visual Studio? ==&lt;br /&gt;
&lt;br /&gt;
Ja.&lt;br /&gt;
&lt;br /&gt;
Wenn Visual Studio und Codex auf demselben Projektverzeichnis arbeiten, kann Codex Änderungen sehen, die zuvor in Visual Studio durchgeführt wurden.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist normalerweise, dass die Datei &#039;&#039;&#039;gespeichert&#039;&#039;&#039; wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio&lt;br /&gt;
     ↓&lt;br /&gt;
Datei speichern&lt;br /&gt;
     ↓&lt;br /&gt;
Datei auf der Festplatte&lt;br /&gt;
     ↓&lt;br /&gt;
Codex liest die aktuelle Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Eine Datei&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
ProjectService.cs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird in Visual Studio geändert und gespeichert.&lt;br /&gt;
&lt;br /&gt;
Wenn Codex anschließend diese Datei bearbeitet, verwendet Codex den aktuellen gespeicherten Stand.&lt;br /&gt;
&lt;br /&gt;
=== Ungespeicherte Änderungen ===&lt;br /&gt;
&lt;br /&gt;
Ungespeicherte Änderungen befinden sich nur im Editor von Visual Studio.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio:&lt;br /&gt;
neue Änderung&lt;br /&gt;
     ↓&lt;br /&gt;
nicht gespeichert&lt;br /&gt;
&lt;br /&gt;
Festplatte:&lt;br /&gt;
alte Version&lt;br /&gt;
&lt;br /&gt;
Codex:&lt;br /&gt;
sieht die alte Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollten Änderungen in Visual Studio gespeichert werden, bevor Codex mit der Bearbeitung beginnt.&lt;br /&gt;
&lt;br /&gt;
== Gleichzeitiges Bearbeiten derselben Datei ==&lt;br /&gt;
&lt;br /&gt;
Problematisch kann es werden, wenn Visual Studio und Codex gleichzeitig dieselbe Datei bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio                 Codex&lt;br /&gt;
     │                          │&lt;br /&gt;
ändert MainPage.xaml     ändert MainPage.xaml&lt;br /&gt;
     │                          │&lt;br /&gt;
nicht gespeichert         speichert Änderung&lt;br /&gt;
     │                          │&lt;br /&gt;
     └──── Konflikt möglich ────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio kann anschließend melden, dass die Datei außerhalb der Entwicklungsumgebung geändert wurde.&lt;br /&gt;
&lt;br /&gt;
Wird danach die noch ungespeicherte Version aus Visual Studio gespeichert, können die Änderungen von Codex überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Empfohlener Workflow mit Codex ==&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio durchführen&lt;br /&gt;
&lt;br /&gt;
# Alle Dateien speichern&lt;br /&gt;
&lt;br /&gt;
# Codex einen Auftrag geben&lt;br /&gt;
&lt;br /&gt;
# Codex die Änderungen durchführen lassen&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio kontrollieren&lt;br /&gt;
&lt;br /&gt;
# Projekt kompilieren&lt;br /&gt;
&lt;br /&gt;
# Anwendung testen&lt;br /&gt;
&lt;br /&gt;
# Git-Diff überprüfen&lt;br /&gt;
&lt;br /&gt;
# Commit erstellen&lt;br /&gt;
&lt;br /&gt;
# Änderungen mit Push zum Remote-Repository übertragen&lt;br /&gt;
&lt;br /&gt;
== Empfehlung ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können problemlos parallel auf demselben Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Dabei sollte folgende Regel beachtet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Visual Studio und Codex sollten möglichst nicht gleichzeitig dieselbe Datei bearbeiten.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vor der Arbeit mit Codex sollten alle Änderungen in Visual Studio gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio oder Codex ist im normalen Entwicklungsworkflow nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Kurzreferenz ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktion !! Bedeutung !! Lokale Dateien werden verändert?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;Fetch&#039;&#039;&#039;                                                  &lt;br /&gt;
| Neue Informationen und Commits vom Remote-Repository abrufen &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Pull&#039;&#039;&#039;                                                   &lt;br /&gt;
| Remote-Änderungen herunterladen und übernehmen               &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Push&#039;&#039;&#039;                                                   &lt;br /&gt;
| Eigene Commits zum Remote-Repository übertragen              &lt;br /&gt;
| Nein                                                         &lt;br /&gt;
|-                                                            &lt;br /&gt;
| &#039;&#039;&#039;Sync&#039;&#039;&#039;                                                   &lt;br /&gt;
| Pull und anschließend Push durchführen                       &lt;br /&gt;
| Ja                                                           &lt;br /&gt;
|}                                                            &lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Git]]&lt;br /&gt;
[[Kategorie:Visual Studio]]&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:Codex]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=205</id>
		<title>Entwicklung mit Git, Visual Studio und Codex</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Entwicklung_mit_Git,_Visual_Studio_und_Codex&amp;diff=205"/>
		<updated>2026-08-19T11:08:14Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= Git, Visual Studio und Codex =  Diese Seite beschreibt die wichtigsten Git-Funktionen in Visual Studio sowie den empfohlenen Workflow bei der parallelen Verwendung von Visual Studio und Codex für .NET-Projekte.  == Git-Funktionen in Visual Studio ==  Im Fenster &amp;#039;&amp;#039;&amp;#039;Git-Änderungen&amp;#039;&amp;#039;&amp;#039; stellt Visual Studio verschiedene Funktionen zur Synchronisation mit einem Remote-Repository wie GitHub bereit.  === Fetch ===  &amp;#039;&amp;#039;&amp;#039;Fetch&amp;#039;&amp;#039;&amp;#039; lädt Informationen über neue C…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Git, Visual Studio und Codex =&lt;br /&gt;
&lt;br /&gt;
Diese Seite beschreibt die wichtigsten Git-Funktionen in Visual Studio sowie den empfohlenen Workflow bei der parallelen Verwendung von Visual Studio und Codex für .NET-Projekte.&lt;br /&gt;
&lt;br /&gt;
== Git-Funktionen in Visual Studio ==&lt;br /&gt;
&lt;br /&gt;
Im Fenster &#039;&#039;&#039;Git-Änderungen&#039;&#039;&#039; stellt Visual Studio verschiedene Funktionen zur Synchronisation mit einem Remote-Repository wie GitHub bereit.&lt;br /&gt;
&lt;br /&gt;
=== Fetch ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fetch&#039;&#039;&#039; lädt Informationen über neue Commits und Branches vom Remote-Repository herunter, verändert den lokalen Arbeitsstand jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fetch eignet sich, wenn zunächst geprüft werden soll, ob auf dem Remote-Repository neue Änderungen vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Fetch = Änderungen vom Server abrufen, aber noch nicht in den eigenen Branch übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Pull ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pull&#039;&#039;&#039; lädt Änderungen vom Remote-Repository herunter und integriert sie in den aktuell ausgecheckten lokalen Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht entspricht Pull:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fetch&lt;br /&gt;
  +&lt;br /&gt;
Merge bzw. Rebase&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei können Merge-Konflikte entstehen, wenn lokale und entfernte Änderungen dieselben Bereiche einer Datei betreffen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Pull = Änderungen herunterladen und in den eigenen Arbeitsstand übernehmen.&lt;br /&gt;
&lt;br /&gt;
=== Push ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Push&#039;&#039;&#039; überträgt lokal erstellte Commits zum Remote-Repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git push&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nur Änderungen übertragen, die zuvor als Commit gespeichert wurden.&lt;br /&gt;
&lt;br /&gt;
Befinden sich auf dem Remote-Repository bereits neuere Commits, die lokal noch nicht vorhanden sind, kann Git den Push ablehnen. In diesem Fall sollte normalerweise zunächst ein Pull durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Merksatz:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
:Push = Eigene Commits zum Server hochladen.&lt;br /&gt;
&lt;br /&gt;
=== Sync ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sync&#039;&#039;&#039; synchronisiert den lokalen und den entfernten Branch.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht führt Visual Studio dabei folgende Schritte aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zuerst werden also Änderungen vom Remote-Repository übernommen. Anschließend werden die eigenen lokalen Commits hochgeladen.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Optionen ===&lt;br /&gt;
&lt;br /&gt;
Über das Symbol&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
stehen zusätzliche Git-Funktionen und Synchronisationsoptionen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
== Typischer Git-Workflow ==&lt;br /&gt;
&lt;br /&gt;
Ein normaler Arbeitsablauf kann beispielsweise folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pull&lt;br /&gt;
 ↓&lt;br /&gt;
Code bearbeiten&lt;br /&gt;
 ↓&lt;br /&gt;
Änderungen testen&lt;br /&gt;
 ↓&lt;br /&gt;
Commit erstellen&lt;br /&gt;
 ↓&lt;br /&gt;
Push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Optional kann vorher mit &#039;&#039;&#039;Fetch&#039;&#039;&#039; geprüft werden, ob neue Änderungen im Remote-Repository vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
== Visual Studio und Codex gleichzeitig verwenden ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können parallel mit demselben .NET-Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio nach Änderungen durch Codex ist normalerweise &#039;&#039;&#039;nicht erforderlich&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Codex verändert die Dateien direkt im Projektverzeichnis. Visual Studio erkennt externe Dateiänderungen in der Regel automatisch.&lt;br /&gt;
&lt;br /&gt;
Typische Dateien sind beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.cs&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.razor&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.xaml&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.json&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei diesen Dateien ist normalerweise kein erneutes Laden des Projekts notwendig.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen an Projektdateien ===&lt;br /&gt;
&lt;br /&gt;
Bei Änderungen an Dateien wie&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;.csproj&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.sln&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann Visual Studio gegebenenfalls verlangen, das Projekt neu zu laden.&lt;br /&gt;
&lt;br /&gt;
Ein kompletter Neustart von Visual Studio ist normalerweise trotzdem nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Laufende Anwendung und Hot Reload ==&lt;br /&gt;
&lt;br /&gt;
Dass Visual Studio eine geänderte Datei erkennt, bedeutet nicht automatisch, dass die Änderung sofort in einer bereits laufenden Anwendung aktiv wird.&lt;br /&gt;
&lt;br /&gt;
Je nach Änderung kann &#039;&#039;&#039;Hot Reload&#039;&#039;&#039; die Änderung während der Laufzeit übernehmen.&lt;br /&gt;
&lt;br /&gt;
Ist die Änderung nicht mit Hot Reload kompatibel, reicht normalerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Anwendung stoppen&lt;br /&gt;
 ↓&lt;br /&gt;
Projekt neu kompilieren&lt;br /&gt;
 ↓&lt;br /&gt;
Anwendung erneut starten&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio selbst muss dafür nicht neu gestartet werden.&lt;br /&gt;
&lt;br /&gt;
== Erkennt Codex Änderungen aus Visual Studio? ==&lt;br /&gt;
&lt;br /&gt;
Ja.&lt;br /&gt;
&lt;br /&gt;
Wenn Visual Studio und Codex auf demselben Projektverzeichnis arbeiten, kann Codex Änderungen sehen, die zuvor in Visual Studio durchgeführt wurden.&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist normalerweise, dass die Datei &#039;&#039;&#039;gespeichert&#039;&#039;&#039; wurde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio&lt;br /&gt;
     ↓&lt;br /&gt;
Datei speichern&lt;br /&gt;
     ↓&lt;br /&gt;
Datei auf der Festplatte&lt;br /&gt;
     ↓&lt;br /&gt;
Codex liest die aktuelle Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Eine Datei&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
ProjectService.cs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird in Visual Studio geändert und gespeichert.&lt;br /&gt;
&lt;br /&gt;
Wenn Codex anschließend diese Datei bearbeitet, verwendet Codex den aktuellen gespeicherten Stand.&lt;br /&gt;
&lt;br /&gt;
=== Ungespeicherte Änderungen ===&lt;br /&gt;
&lt;br /&gt;
Ungespeicherte Änderungen befinden sich nur im Editor von Visual Studio.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio:&lt;br /&gt;
neue Änderung&lt;br /&gt;
     ↓&lt;br /&gt;
nicht gespeichert&lt;br /&gt;
&lt;br /&gt;
Festplatte:&lt;br /&gt;
alte Version&lt;br /&gt;
&lt;br /&gt;
Codex:&lt;br /&gt;
sieht die alte Version&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deshalb sollten Änderungen in Visual Studio gespeichert werden, bevor Codex mit der Bearbeitung beginnt.&lt;br /&gt;
&lt;br /&gt;
== Gleichzeitiges Bearbeiten derselben Datei ==&lt;br /&gt;
&lt;br /&gt;
Problematisch kann es werden, wenn Visual Studio und Codex gleichzeitig dieselbe Datei bearbeiten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Visual Studio                 Codex&lt;br /&gt;
     │                          │&lt;br /&gt;
ändert MainPage.xaml     ändert MainPage.xaml&lt;br /&gt;
     │                          │&lt;br /&gt;
nicht gespeichert         speichert Änderung&lt;br /&gt;
     │                          │&lt;br /&gt;
     └──── Konflikt möglich ────┘&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Visual Studio kann anschließend melden, dass die Datei außerhalb der Entwicklungsumgebung geändert wurde.&lt;br /&gt;
&lt;br /&gt;
Wird danach die noch ungespeicherte Version aus Visual Studio gespeichert, können die Änderungen von Codex überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
== Empfohlener Workflow mit Codex ==&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio durchführen&lt;br /&gt;
&lt;br /&gt;
# Alle Dateien speichern&lt;br /&gt;
&lt;br /&gt;
# Codex einen Auftrag geben&lt;br /&gt;
&lt;br /&gt;
# Codex die Änderungen durchführen lassen&lt;br /&gt;
&lt;br /&gt;
# Änderungen in Visual Studio kontrollieren&lt;br /&gt;
&lt;br /&gt;
# Projekt kompilieren&lt;br /&gt;
&lt;br /&gt;
# Anwendung testen&lt;br /&gt;
&lt;br /&gt;
# Git-Diff überprüfen&lt;br /&gt;
&lt;br /&gt;
# Commit erstellen&lt;br /&gt;
&lt;br /&gt;
# Änderungen mit Push zum Remote-Repository übertragen&lt;br /&gt;
&lt;br /&gt;
== Empfehlung ==&lt;br /&gt;
&lt;br /&gt;
Visual Studio und Codex können problemlos parallel auf demselben Projekt arbeiten.&lt;br /&gt;
&lt;br /&gt;
Dabei sollte folgende Regel beachtet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;Visual Studio und Codex sollten möglichst nicht gleichzeitig dieselbe Datei bearbeiten.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vor der Arbeit mit Codex sollten alle Änderungen in Visual Studio gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Ein Neustart von Visual Studio oder Codex ist im normalen Entwicklungsworkflow nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
== Kurzreferenz ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion&lt;br /&gt;
! Bedeutung&lt;br /&gt;
&lt;br /&gt;
| ! Lokale Dateien werden verändert?                           |&lt;br /&gt;
| ------------------------------------------------------------ |&lt;br /&gt;
| &#039;&#039;&#039;Fetch&#039;&#039;&#039;                                                  |&lt;br /&gt;
| Neue Informationen und Commits vom Remote-Repository abrufen |&lt;br /&gt;
| Nein                                                         |&lt;br /&gt;
| -                                                            |&lt;br /&gt;
| &#039;&#039;&#039;Pull&#039;&#039;&#039;                                                   |&lt;br /&gt;
| Remote-Änderungen herunterladen und übernehmen               |&lt;br /&gt;
| Ja                                                           |&lt;br /&gt;
| -                                                            |&lt;br /&gt;
| &#039;&#039;&#039;Push&#039;&#039;&#039;                                                   |&lt;br /&gt;
| Eigene Commits zum Remote-Repository übertragen              |&lt;br /&gt;
| Nein                                                         |&lt;br /&gt;
| -                                                            |&lt;br /&gt;
| &#039;&#039;&#039;Sync&#039;&#039;&#039;                                                   |&lt;br /&gt;
| Pull und anschließend Push durchführen                       |&lt;br /&gt;
| Ja                                                           |&lt;br /&gt;
| }                                                            |&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Git]]&lt;br /&gt;
[[Kategorie:Visual Studio]]&lt;br /&gt;
[[Kategorie:.NET]]&lt;br /&gt;
[[Kategorie:Codex]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=204</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=204"/>
		<updated>2026-08-19T11:08:01Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Programmierung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Software-Entwicklung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[Entwicklung mit Git, Visual Studio und Codex]]&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[Lineare Gewichtung]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== KI und Machine Learning==&lt;br /&gt;
* [[Beam Search]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Git&amp;diff=203</id>
		<title>Git</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Git&amp;diff=203"/>
		<updated>2026-06-04T10:27:27Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= Git =  &amp;#039;&amp;#039;&amp;#039;Git&amp;#039;&amp;#039;&amp;#039; ist ein verteiltes Versionsverwaltungssystem, das zur Verwaltung von Änderungen an Dateien und Quellcode eingesetzt wird. Es ermöglicht, verschiedene Versionen eines Projekts zu speichern, Änderungen nachzuvollziehen, ältere Zustände wiederherzustellen und gemeinsam an Softwareprojekten zu arbeiten.  Git wird besonders häufig in der Softwareentwicklung verwendet, eignet sich aber grundsätzlich für alle textbasierten Dateien,…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Git =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Git&#039;&#039;&#039; ist ein verteiltes Versionsverwaltungssystem, das zur Verwaltung von Änderungen an Dateien und Quellcode eingesetzt wird. Es ermöglicht, verschiedene Versionen eines Projekts zu speichern, Änderungen nachzuvollziehen, ältere Zustände wiederherzustellen und gemeinsam an Softwareprojekten zu arbeiten.&lt;br /&gt;
&lt;br /&gt;
Git wird besonders häufig in der [[Softwareentwicklung]] verwendet, eignet sich aber grundsätzlich für alle textbasierten Dateien, zum Beispiel Konfigurationsdateien, Dokumentationen oder Skripte.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Bei der Entwicklung von Software ändern sich Dateien ständig. Ohne Versionsverwaltung ist es schwierig nachzuvollziehen:&lt;br /&gt;
&lt;br /&gt;
* wann eine Änderung vorgenommen wurde&lt;br /&gt;
* wer eine Änderung vorgenommen hat&lt;br /&gt;
* warum eine Änderung vorgenommen wurde&lt;br /&gt;
* welche Dateien betroffen waren&lt;br /&gt;
* wie ein früherer Zustand wiederhergestellt werden kann&lt;br /&gt;
&lt;br /&gt;
Git löst dieses Problem, indem Änderungen in sogenannten &#039;&#039;&#039;Commits&#039;&#039;&#039; gespeichert werden. Jeder Commit beschreibt einen bestimmten Zustand des Projekts.&lt;br /&gt;
&lt;br /&gt;
== Verteilte Versionsverwaltung ==&lt;br /&gt;
&lt;br /&gt;
Git ist ein &#039;&#039;&#039;verteiltes Versionsverwaltungssystem&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Jeder Entwickler besitzt eine vollständige Kopie des Repositories einschließlich der gesamten Historie.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu zentralen Systemen wie SVN ist Git nicht dauerhaft auf einen zentralen Server angewiesen.&lt;br /&gt;
&lt;br /&gt;
Vorteile der verteilten Arbeitsweise:&lt;br /&gt;
&lt;br /&gt;
* Arbeiten ohne permanente Internetverbindung&lt;br /&gt;
* Lokale Commits ohne Serverzugriff&lt;br /&gt;
* Schnelle Operationen&lt;br /&gt;
* Hohe Ausfallsicherheit&lt;br /&gt;
* Einfache parallele Entwicklung&lt;br /&gt;
&lt;br /&gt;
Ein zentraler Server wird häufig trotzdem verwendet, zum Beispiel über Plattformen wie GitHub, GitLab oder Bitbucket. Dieser Server dient dann vor allem der Zusammenarbeit und Synchronisation.&lt;br /&gt;
&lt;br /&gt;
== Repository ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Repository&#039;&#039;&#039; ist der Speicherort eines Git-Projekts. Es enthält:&lt;br /&gt;
&lt;br /&gt;
* die Projektdateien&lt;br /&gt;
* die Commit-Historie&lt;br /&gt;
* Branches&lt;br /&gt;
* Tags&lt;br /&gt;
* Konfigurationsinformationen&lt;br /&gt;
* Metadaten von Git&lt;br /&gt;
&lt;br /&gt;
Ein neues Repository wird mit folgendem Befehl erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git init&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein vorhandenes Repository wird mit folgendem Befehl kopiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git clone https://example.org/projekt.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Begriffe ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Begriff&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| Repository&lt;br /&gt;
| Git-Projekt mit Dateien, Historie und Metadaten&lt;br /&gt;
|-&lt;br /&gt;
| Commit&lt;br /&gt;
| Gespeicherter Zustand des Projekts&lt;br /&gt;
|-&lt;br /&gt;
| Branch&lt;br /&gt;
| Entwicklungszweig innerhalb eines Repositories&lt;br /&gt;
|-&lt;br /&gt;
| Merge&lt;br /&gt;
| Zusammenführen zweier Branches&lt;br /&gt;
|-&lt;br /&gt;
| Rebase&lt;br /&gt;
| Neuaufbau einer Commit-Historie auf Basis eines anderen Branches&lt;br /&gt;
|-&lt;br /&gt;
| Remote&lt;br /&gt;
| Entferntes Repository, zum Beispiel auf GitHub oder GitLab&lt;br /&gt;
|-&lt;br /&gt;
| Clone&lt;br /&gt;
| Lokale Kopie eines entfernten Repositories&lt;br /&gt;
|-&lt;br /&gt;
| Pull&lt;br /&gt;
| Änderungen aus einem entfernten Repository herunterladen und integrieren&lt;br /&gt;
|-&lt;br /&gt;
| Push&lt;br /&gt;
| Lokale Änderungen in ein entferntes Repository hochladen&lt;br /&gt;
|-&lt;br /&gt;
| Tag&lt;br /&gt;
| Markierung eines bestimmten Commits, häufig für Versionen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Arbeitsbereiche in Git ==&lt;br /&gt;
&lt;br /&gt;
Git unterscheidet mehrere Arbeitsbereiche.&lt;br /&gt;
&lt;br /&gt;
=== Working Directory ===&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Working Directory&#039;&#039;&#039; enthält die Dateien, an denen aktuell gearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Datei bearbeitet wird, befindet sich die Änderung zunächst nur im Working Directory.&lt;br /&gt;
&lt;br /&gt;
=== Staging Area ===&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Staging Area&#039;&#039;&#039; ist ein Zwischenspeicher für Änderungen, die in den nächsten Commit aufgenommen werden sollen.&lt;br /&gt;
&lt;br /&gt;
Dateien werden mit folgendem Befehl zur Staging Area hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add datei.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alle geänderten Dateien können mit folgendem Befehl hinzugefügt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add .&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lokales Repository ===&lt;br /&gt;
&lt;br /&gt;
Das lokale Repository enthält die dauerhaft gespeicherten Commits.&lt;br /&gt;
&lt;br /&gt;
Ein Commit wird mit folgendem Befehl erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git commit -m &amp;quot;Beschreibung der Änderung&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Typischer Git-Arbeitsablauf ==&lt;br /&gt;
&lt;br /&gt;
Ein einfacher Arbeitsablauf sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
# Dateien bearbeiten&lt;br /&gt;
# Änderungen prüfen&lt;br /&gt;
# Änderungen zur Staging Area hinzufügen&lt;br /&gt;
# Commit erstellen&lt;br /&gt;
# Änderungen optional auf einen Server übertragen&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
git add .&lt;br /&gt;
git commit -m &amp;quot;Neue Funktion implementiert&amp;quot;&lt;br /&gt;
git push&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Status anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Der aktuelle Zustand des Repositories wird mit folgendem Befehl angezeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git status&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl zeigt unter anderem:&lt;br /&gt;
&lt;br /&gt;
* geänderte Dateien&lt;br /&gt;
* neue Dateien&lt;br /&gt;
* gelöschte Dateien&lt;br /&gt;
* Dateien in der Staging Area&lt;br /&gt;
* den aktuellen Branch&lt;br /&gt;
&lt;br /&gt;
== Änderungen anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Noch nicht vorgemerkte Änderungen können mit folgendem Befehl angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git diff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Änderungen in der Staging Area können so angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git diff --staged&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Commits ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Commit&#039;&#039;&#039; ist eine gespeicherte Momentaufnahme des Projekts.&lt;br /&gt;
&lt;br /&gt;
Ein Commit enthält:&lt;br /&gt;
&lt;br /&gt;
* eine eindeutige ID&lt;br /&gt;
* Autorinformationen&lt;br /&gt;
* Datum und Uhrzeit&lt;br /&gt;
* Commit-Nachricht&lt;br /&gt;
* Verweis auf vorherige Commits&lt;br /&gt;
* Änderungen am Projektzustand&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git commit -m &amp;quot;Fehler in der Benutzeranmeldung behoben&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine gute Commit-Nachricht sollte kurz und verständlich beschreiben, was geändert wurde.&lt;br /&gt;
&lt;br /&gt;
== Commit-Historie anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Die Commit-Historie wird mit folgendem Befehl angezeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git log&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine kompaktere Darstellung erhält man mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git log --oneline&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine grafische Übersicht über Branches kann so angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git log --oneline --graph --all&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Branches ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Branch&#039;&#039;&#039; ist ein Entwicklungszweig.&lt;br /&gt;
&lt;br /&gt;
Branches ermöglichen es, parallel an unterschiedlichen Aufgaben zu arbeiten, ohne den Hauptzweig direkt zu verändern.&lt;br /&gt;
&lt;br /&gt;
Typische Branches sind:&lt;br /&gt;
&lt;br /&gt;
* main&lt;br /&gt;
* develop&lt;br /&gt;
* feature/login&lt;br /&gt;
* bugfix/navigation&lt;br /&gt;
* release/1.0&lt;br /&gt;
&lt;br /&gt;
== Branch erstellen ==&lt;br /&gt;
&lt;br /&gt;
Ein neuer Branch wird mit folgendem Befehl erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git branch feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Wechseln auf den Branch wird verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git checkout feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In modernen Git-Versionen kann stattdessen auch verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Branch kann gleichzeitig erstellt und aktiviert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch -c feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Branches anzeigen ==&lt;br /&gt;
&lt;br /&gt;
Alle lokalen Branches werden mit folgendem Befehl angezeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git branch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alle lokalen und entfernten Branches werden so angezeigt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git branch -a&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Merge ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Merge&#039;&#039;&#039; führt Änderungen aus einem Branch in einen anderen Branch zusammen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch main&lt;br /&gt;
git merge feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei werden die Änderungen aus dem Branch &amp;lt;code&amp;gt;feature-login&amp;lt;/code&amp;gt; in den Branch &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; übernommen.&lt;br /&gt;
&lt;br /&gt;
== Merge-Konflikte ==&lt;br /&gt;
&lt;br /&gt;
Ein Merge-Konflikt entsteht, wenn Git Änderungen nicht automatisch zusammenführen kann.&lt;br /&gt;
&lt;br /&gt;
Das passiert zum Beispiel, wenn zwei Branches dieselbe Zeile einer Datei unterschiedlich verändert haben.&lt;br /&gt;
&lt;br /&gt;
Git markiert die betroffenen Stellen in der Datei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD&lt;br /&gt;
Aktuelle Version&lt;br /&gt;
=======&lt;br /&gt;
Andere Version&lt;br /&gt;
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Entwickler muss den Konflikt manuell auflösen. Danach wird die Datei erneut hinzugefügt und der Merge abgeschlossen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add datei.txt&lt;br /&gt;
git commit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rebase ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Rebase&#039;&#039;&#039; setzt die Commits eines Branches auf eine neue Basis.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch feature-login&lt;br /&gt;
git rebase main&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch werden die Commits des Feature-Branches so neu angeordnet, als wären sie direkt auf dem aktuellen Stand von &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; entstanden.&lt;br /&gt;
&lt;br /&gt;
=== Unterschied zwischen Merge und Rebase ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Beschreibung&lt;br /&gt;
! Vorteil&lt;br /&gt;
! Nachteil&lt;br /&gt;
|-&lt;br /&gt;
| Merge&lt;br /&gt;
| Führt zwei Branches zusammen und erzeugt bei Bedarf einen Merge-Commit&lt;br /&gt;
| Historie bleibt vollständig erhalten&lt;br /&gt;
| Historie kann unübersichtlich werden&lt;br /&gt;
|-&lt;br /&gt;
| Rebase&lt;br /&gt;
| Setzt Commits auf eine neue Basis&lt;br /&gt;
| Lineare, saubere Historie&lt;br /&gt;
| Kann problematisch sein, wenn bereits veröffentlichte Commits verändert werden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Als Faustregel gilt: Rebase sollte nicht unüberlegt auf Branches angewendet werden, die bereits mit anderen Personen geteilt wurden.&lt;br /&gt;
&lt;br /&gt;
== Remote Repositories ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Remote Repository&#039;&#039;&#039; ist ein entferntes Repository, zum Beispiel auf einem Server.&lt;br /&gt;
&lt;br /&gt;
Typische Plattformen sind:&lt;br /&gt;
&lt;br /&gt;
* GitHub&lt;br /&gt;
* GitLab&lt;br /&gt;
* Bitbucket&lt;br /&gt;
* Gitea&lt;br /&gt;
* Forgejo&lt;br /&gt;
&lt;br /&gt;
Die Verbindung zu einem entfernten Repository wird häufig unter dem Namen &amp;lt;code&amp;gt;origin&amp;lt;/code&amp;gt; gespeichert.&lt;br /&gt;
&lt;br /&gt;
Remotes anzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git remote -v&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Remote hinzufügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git remote add origin https://example.org/projekt.git&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Push ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;push&#039;&#039;&#039; werden lokale Commits in ein entferntes Repository hochgeladen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git push origin main&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beim ersten Push eines neuen Branches wird häufig verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git push -u origin feature-login&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Option &amp;lt;code&amp;gt;-u&amp;lt;/code&amp;gt; setzt den entfernten Branch als Standardziel für spätere Push- und Pull-Befehle.&lt;br /&gt;
&lt;br /&gt;
== Pull ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;pull&#039;&#039;&#039; werden Änderungen aus einem entfernten Repository heruntergeladen und in den aktuellen Branch integriert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git pull&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Intern entspricht &amp;lt;code&amp;gt;git pull&amp;lt;/code&amp;gt; meistens einer Kombination aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
git merge&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nach Konfiguration kann statt Merge auch Rebase verwendet werden.&lt;br /&gt;
&lt;br /&gt;
== Fetch ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;fetch&#039;&#039;&#039; werden Änderungen aus einem entfernten Repository heruntergeladen, aber noch nicht in den aktuellen Branch integriert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch kann man zunächst prüfen, welche Änderungen verfügbar sind.&lt;br /&gt;
&lt;br /&gt;
== Tags ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Tag&#039;&#039;&#039; markiert einen bestimmten Commit.&lt;br /&gt;
&lt;br /&gt;
Tags werden häufig für Softwareversionen verwendet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag v1.0.0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Tag kann auf den Server übertragen werden mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git push origin v1.0.0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alle Tags anzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git tag&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== .gitignore ==&lt;br /&gt;
&lt;br /&gt;
Die Datei &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; legt fest, welche Dateien Git ignorieren soll.&lt;br /&gt;
&lt;br /&gt;
Typische Beispiele:&lt;br /&gt;
&lt;br /&gt;
* temporäre Dateien&lt;br /&gt;
* Build-Artefakte&lt;br /&gt;
* Logdateien&lt;br /&gt;
* lokale Konfigurationsdateien&lt;br /&gt;
* Abhängigkeiten wie &amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
node_modules/&lt;br /&gt;
*.log&lt;br /&gt;
.env&lt;br /&gt;
dist/&lt;br /&gt;
build/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt;-Datei verhindert, dass unnötige oder sensible Dateien versehentlich versioniert werden.&lt;br /&gt;
&lt;br /&gt;
== Dateien aus Git entfernen ==&lt;br /&gt;
&lt;br /&gt;
Eine Datei kann aus Git entfernt werden mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git rm datei.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll eine Datei nur aus der Versionsverwaltung entfernt werden, aber lokal erhalten bleiben, verwendet man:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git rm --cached datei.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das ist besonders nützlich, wenn eine Datei versehentlich versioniert wurde und anschließend in &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; eingetragen wird.&lt;br /&gt;
&lt;br /&gt;
== Änderungen rückgängig machen ==&lt;br /&gt;
&lt;br /&gt;
Git bietet verschiedene Möglichkeiten, Änderungen rückgängig zu machen.&lt;br /&gt;
&lt;br /&gt;
=== Nicht gespeicherte Änderung verwerfen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git restore datei.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datei aus der Staging Area entfernen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git restore --staged datei.txt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Letzten Commit ändern ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git commit --amend&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl verändert den letzten Commit. Er sollte vorsichtig verwendet werden, wenn der Commit bereits veröffentlicht wurde.&lt;br /&gt;
&lt;br /&gt;
=== Commit rückgängig machen ===&lt;br /&gt;
&lt;br /&gt;
Ein veröffentlichter Commit sollte meistens mit &amp;lt;code&amp;gt;git revert&amp;lt;/code&amp;gt; rückgängig gemacht werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git revert COMMIT_ID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei entsteht ein neuer Commit, der die Änderungen des alten Commits rückgängig macht.&lt;br /&gt;
&lt;br /&gt;
== Reset ==&lt;br /&gt;
&lt;br /&gt;
Mit &amp;lt;code&amp;gt;git reset&amp;lt;/code&amp;gt; kann der aktuelle Branch auf einen früheren Zustand zurückgesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git reset --soft HEAD~1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl entfernt den letzten Commit, behält die Änderungen aber in der Staging Area.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git reset --mixed HEAD~1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl entfernt den letzten Commit und nimmt die Änderungen aus der Staging Area heraus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git reset --hard HEAD~1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl entfernt den letzten Commit und verwirft die Änderungen vollständig.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; &amp;lt;code&amp;gt;git reset --hard&amp;lt;/code&amp;gt; kann nicht gespeicherte Arbeit dauerhaft löschen.&lt;br /&gt;
&lt;br /&gt;
== Stash ==&lt;br /&gt;
&lt;br /&gt;
Mit &#039;&#039;&#039;stash&#039;&#039;&#039; können aktuelle Änderungen vorübergehend gespeichert werden, ohne direkt einen Commit zu erstellen.&lt;br /&gt;
&lt;br /&gt;
Änderungen speichern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git stash&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Gespeicherte Änderungen anzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git stash list&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Letzten Stash wiederherstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git stash pop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Stash wiederherstellen, aber nicht löschen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git stash apply&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Git-Konfiguration ==&lt;br /&gt;
&lt;br /&gt;
Vor der Nutzung von Git sollten Name und E-Mail-Adresse eingerichtet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git config --global user.name &amp;quot;Max Mustermann&amp;quot;&lt;br /&gt;
git config --global user.email &amp;quot;max@example.org&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aktuelle Konfiguration anzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git config --list&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Git-Objektmodell ==&lt;br /&gt;
&lt;br /&gt;
Git speichert Inhalte intern als Objekte.&lt;br /&gt;
&lt;br /&gt;
Wichtige Objekttypen sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Objekttyp&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| Blob&lt;br /&gt;
| Inhalt einer Datei&lt;br /&gt;
|-&lt;br /&gt;
| Tree&lt;br /&gt;
| Verzeichnisstruktur&lt;br /&gt;
|-&lt;br /&gt;
| Commit&lt;br /&gt;
| Projektzustand mit Metadaten und Verweisen&lt;br /&gt;
|-&lt;br /&gt;
| Tag&lt;br /&gt;
| Markierung eines bestimmten Objekts&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Git speichert nicht einfach nur Unterschiede zwischen Dateien, sondern verwaltet Projektzustände über ein internes Objektmodell. Zur Speicheroptimierung kann Git Objekte später komprimieren und effizient ablegen.&lt;br /&gt;
&lt;br /&gt;
== Hashes ==&lt;br /&gt;
&lt;br /&gt;
Git identifiziert Objekte über Hashwerte.&lt;br /&gt;
&lt;br /&gt;
Ein Commit besitzt beispielsweise eine eindeutige Commit-ID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
a3f5c9d7e1b2...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese ID wird aus dem Inhalt und den Metadaten erzeugt. Ändert sich der Inhalt, ändert sich auch der Hash.&lt;br /&gt;
&lt;br /&gt;
Historisch verwendet Git SHA-1. Neuere Git-Versionen unterstützen auch SHA-256-Repositories.&lt;br /&gt;
&lt;br /&gt;
== HEAD ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; bezeichnet in Git den aktuell ausgecheckten Commit.&lt;br /&gt;
&lt;br /&gt;
Meist zeigt &amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; auf den aktuellen Branch.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
HEAD -&amp;gt; main&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn &amp;lt;code&amp;gt;HEAD&amp;lt;/code&amp;gt; direkt auf einen Commit zeigt und nicht auf einen Branch, spricht man von einem &#039;&#039;&#039;detached HEAD&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Detached HEAD ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;detached HEAD&#039;&#039;&#039; entsteht, wenn ein bestimmter Commit direkt ausgecheckt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git checkout COMMIT_ID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In diesem Zustand befindet man sich nicht auf einem Branch. Neue Commits können dadurch leicht verloren gehen, wenn kein neuer Branch erstellt wird.&lt;br /&gt;
&lt;br /&gt;
Sicherer ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch -c test-branch COMMIT_ID&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Submodule ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Submodule&#039;&#039;&#039; ermöglicht es, ein anderes Git-Repository innerhalb eines Repositories einzubinden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git submodule add https://example.org/bibliothek.git libs/bibliothek&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Submodule werden häufig verwendet, wenn externe Bibliotheken oder gemeinsam genutzte Komponenten eingebunden werden sollen.&lt;br /&gt;
&lt;br /&gt;
Nachteile von Submodulen:&lt;br /&gt;
&lt;br /&gt;
* zusätzliche Komplexität&lt;br /&gt;
* getrennte Historien&lt;br /&gt;
* Fehleranfälligkeit bei Updates&lt;br /&gt;
* schwieriger für Einsteiger&lt;br /&gt;
&lt;br /&gt;
== Git Hooks ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Git Hooks&#039;&#039;&#039; sind Skripte, die bei bestimmten Git-Ereignissen automatisch ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* vor einem Commit&lt;br /&gt;
* nach einem Commit&lt;br /&gt;
* vor einem Push&lt;br /&gt;
* nach einem Merge&lt;br /&gt;
&lt;br /&gt;
Typische Einsatzzwecke:&lt;br /&gt;
&lt;br /&gt;
* Codeformatierung prüfen&lt;br /&gt;
* Tests ausführen&lt;br /&gt;
* Commit-Nachrichten validieren&lt;br /&gt;
* Sicherheitsprüfungen durchführen&lt;br /&gt;
&lt;br /&gt;
Hooks befinden sich im Verzeichnis:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.git/hooks/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Typische Workflows ==&lt;br /&gt;
&lt;br /&gt;
=== Feature-Branch-Workflow ===&lt;br /&gt;
&lt;br /&gt;
Beim Feature-Branch-Workflow wird für jede neue Funktion ein eigener Branch erstellt.&lt;br /&gt;
&lt;br /&gt;
Ablauf:&lt;br /&gt;
&lt;br /&gt;
# Branch von &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; erstellen&lt;br /&gt;
# Funktion implementieren&lt;br /&gt;
# Änderungen committen&lt;br /&gt;
# Branch pushen&lt;br /&gt;
# Merge Request oder Pull Request erstellen&lt;br /&gt;
# Code Review durchführen&lt;br /&gt;
# Branch in &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; mergen&lt;br /&gt;
&lt;br /&gt;
Dieser Workflow ist in vielen Teams Standard.&lt;br /&gt;
&lt;br /&gt;
=== Gitflow ===&lt;br /&gt;
&lt;br /&gt;
Gitflow verwendet mehrere feste Branch-Typen:&lt;br /&gt;
&lt;br /&gt;
* main&lt;br /&gt;
* develop&lt;br /&gt;
* feature/*&lt;br /&gt;
* release/*&lt;br /&gt;
* hotfix/*&lt;br /&gt;
&lt;br /&gt;
Gitflow eignet sich besonders für Projekte mit klaren Release-Zyklen.&lt;br /&gt;
&lt;br /&gt;
Für kleinere Projekte kann Gitflow jedoch unnötig komplex sein.&lt;br /&gt;
&lt;br /&gt;
=== Trunk-Based Development ===&lt;br /&gt;
&lt;br /&gt;
Beim Trunk-Based Development arbeiten Entwickler möglichst direkt auf einem Hauptbranch oder verwenden nur sehr kurzlebige Branches.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&lt;br /&gt;
* schnelle Integration&lt;br /&gt;
* weniger Merge-Konflikte&lt;br /&gt;
* gut geeignet für Continuous Integration&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
&lt;br /&gt;
* erfordert gute Tests&lt;br /&gt;
* erfordert disziplinierte Entwicklung&lt;br /&gt;
* unfertige Funktionen müssen oft über Feature Flags abgesichert werden&lt;br /&gt;
&lt;br /&gt;
== Git und Plattformen ==&lt;br /&gt;
&lt;br /&gt;
Git selbst ist ein Kommandozeilenwerkzeug und Versionsverwaltungssystem.&lt;br /&gt;
&lt;br /&gt;
Plattformen wie GitHub oder GitLab sind zusätzliche Dienste, die Git-Repositories hosten und weitere Funktionen bereitstellen.&lt;br /&gt;
&lt;br /&gt;
Typische Zusatzfunktionen solcher Plattformen:&lt;br /&gt;
&lt;br /&gt;
* Pull Requests oder Merge Requests&lt;br /&gt;
* Issue-Tracking&lt;br /&gt;
* Projektmanagement&lt;br /&gt;
* Continuous Integration&lt;br /&gt;
* Code Reviews&lt;br /&gt;
* Wiki-Funktionen&lt;br /&gt;
* Rechteverwaltung&lt;br /&gt;
&lt;br /&gt;
Git und GitHub sind daher nicht dasselbe. Git ist das Versionsverwaltungssystem, GitHub ist eine Plattform zur Zusammenarbeit mit Git.&lt;br /&gt;
&lt;br /&gt;
== Häufige Befehle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Befehl&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git init&amp;lt;/code&amp;gt;&lt;br /&gt;
| Neues Repository erstellen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git clone&amp;lt;/code&amp;gt;&lt;br /&gt;
| Repository kopieren&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git status&amp;lt;/code&amp;gt;&lt;br /&gt;
| Aktuellen Zustand anzeigen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git add&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen zur Staging Area hinzufügen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git commit&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen speichern&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git log&amp;lt;/code&amp;gt;&lt;br /&gt;
| Commit-Historie anzeigen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git branch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Branches anzeigen oder erstellen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git switch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Branch wechseln&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git merge&amp;lt;/code&amp;gt;&lt;br /&gt;
| Branches zusammenführen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git rebase&amp;lt;/code&amp;gt;&lt;br /&gt;
| Commits auf neue Basis setzen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git fetch&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen vom Remote herunterladen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git pull&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen herunterladen und integrieren&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git push&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen hochladen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git stash&amp;lt;/code&amp;gt;&lt;br /&gt;
| Änderungen vorübergehend speichern&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;git tag&amp;lt;/code&amp;gt;&lt;br /&gt;
| Version markieren&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Best Practices ==&lt;br /&gt;
&lt;br /&gt;
=== Kleine Commits erstellen ===&lt;br /&gt;
&lt;br /&gt;
Commits sollten möglichst kleine, logisch zusammenhängende Änderungen enthalten.&lt;br /&gt;
&lt;br /&gt;
Schlecht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git commit -m &amp;quot;Alles geändert&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besser:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git commit -m &amp;quot;Validierung für Login-Formular ergänzt&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Aussagekräftige Commit-Nachrichten verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine gute Commit-Nachricht beschreibt, was geändert wurde und idealerweise warum.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Fehler beim Speichern leerer Formulare behoben&lt;br /&gt;
* Benutzerrolle zur Rechteprüfung hinzugefügt&lt;br /&gt;
* Datenbankmigration für Produkte erstellt&lt;br /&gt;
&lt;br /&gt;
=== Branches sinnvoll benennen ===&lt;br /&gt;
&lt;br /&gt;
Gute Branchnamen sind kurz und beschreibend.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* feature/login&lt;br /&gt;
* bugfix/navbar&lt;br /&gt;
* hotfix/payment-error&lt;br /&gt;
* docs/git-grundlagen&lt;br /&gt;
&lt;br /&gt;
=== Regelmäßig synchronisieren ===&lt;br /&gt;
&lt;br /&gt;
In Teamprojekten sollte regelmäßig mit dem entfernten Repository synchronisiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git fetch&lt;br /&gt;
git pull&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch werden Konflikte frühzeitig erkannt.&lt;br /&gt;
&lt;br /&gt;
=== Keine sensiblen Daten committen ===&lt;br /&gt;
&lt;br /&gt;
Nicht in ein Repository gehören:&lt;br /&gt;
&lt;br /&gt;
* Passwörter&lt;br /&gt;
* API-Schlüssel&lt;br /&gt;
* private Zertifikate&lt;br /&gt;
* Datenbankzugänge&lt;br /&gt;
* persönliche Zugangsdaten&lt;br /&gt;
&lt;br /&gt;
Solche Dateien sollten über &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; ausgeschlossen werden.&lt;br /&gt;
&lt;br /&gt;
== Typische Fehler ==&lt;br /&gt;
&lt;br /&gt;
=== Zu große Commits ===&lt;br /&gt;
&lt;br /&gt;
Sehr große Commits sind schwer zu prüfen und schwer rückgängig zu machen.&lt;br /&gt;
&lt;br /&gt;
=== Unklare Commit-Nachrichten ===&lt;br /&gt;
&lt;br /&gt;
Nachrichten wie „Update“, „Fix“ oder „Änderungen“ sind wenig hilfreich.&lt;br /&gt;
&lt;br /&gt;
=== Direktes Arbeiten auf main ===&lt;br /&gt;
&lt;br /&gt;
In Teamprojekten sollte nicht immer direkt auf dem Hauptbranch gearbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Feature-Branches erhöhen Übersichtlichkeit und Sicherheit.&lt;br /&gt;
&lt;br /&gt;
=== Secrets im Repository ===&lt;br /&gt;
&lt;br /&gt;
Ein einmal veröffentlichter API-Schlüssel sollte als kompromittiert betrachtet und ersetzt werden.&lt;br /&gt;
&lt;br /&gt;
Das Entfernen aus späteren Commits reicht nicht aus, da der Schlüssel weiterhin in der Historie vorhanden sein kann.&lt;br /&gt;
&lt;br /&gt;
== Vorteile von Git ==&lt;br /&gt;
&lt;br /&gt;
* sehr schnell bei lokalen Operationen&lt;br /&gt;
* verteilte Arbeitsweise&lt;br /&gt;
* starke Unterstützung für Branches&lt;br /&gt;
* gute Nachvollziehbarkeit von Änderungen&lt;br /&gt;
* große Verbreitung&lt;br /&gt;
* Integration in viele Entwicklungsumgebungen&lt;br /&gt;
* geeignet für kleine und große Projekte&lt;br /&gt;
&lt;br /&gt;
== Nachteile von Git ==&lt;br /&gt;
&lt;br /&gt;
* für Einsteiger teilweise komplex&lt;br /&gt;
* viele Befehle besitzen unterschiedliche Modi&lt;br /&gt;
* Merge-Konflikte können schwierig sein&lt;br /&gt;
* falsche Nutzung von Reset oder Rebase kann Arbeit zerstören&lt;br /&gt;
* Binärdateien werden weniger effizient verwaltet als Textdateien&lt;br /&gt;
&lt;br /&gt;
== Git in der Softwareentwicklung ==&lt;br /&gt;
&lt;br /&gt;
In modernen Softwareprojekten ist Git häufig eng mit weiteren Entwicklungsprozessen verbunden.&lt;br /&gt;
&lt;br /&gt;
Dazu gehören:&lt;br /&gt;
&lt;br /&gt;
* Code Reviews&lt;br /&gt;
* Continuous Integration&lt;br /&gt;
* automatisierte Tests&lt;br /&gt;
* Deployment-Pipelines&lt;br /&gt;
* Release-Management&lt;br /&gt;
* Issue-Tracking&lt;br /&gt;
* Dokumentation&lt;br /&gt;
&lt;br /&gt;
Git bildet damit nicht nur die technische Grundlage für Versionsverwaltung, sondern auch einen wichtigen Bestandteil professioneller Entwicklungsprozesse.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Einfaches Projekt mit Git verwalten ==&lt;br /&gt;
&lt;br /&gt;
Ein neues Projekt wird erstellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mkdir mein-projekt&lt;br /&gt;
cd mein-projekt&lt;br /&gt;
git init&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Datei wird angelegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
echo &amp;quot;# Mein Projekt&amp;quot; &amp;gt; README.md&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Datei wird versioniert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add README.md&lt;br /&gt;
git commit -m &amp;quot;README erstellt&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Remote wird hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git remote add origin https://example.org/mein-projekt.git&lt;br /&gt;
git push -u origin main&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Arbeiten mit Feature-Branch ==&lt;br /&gt;
&lt;br /&gt;
Neuen Branch erstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git switch -c feature-benutzerprofil&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Änderungen durchführen und committen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git add .&lt;br /&gt;
git commit -m &amp;quot;Benutzerprofil ergänzt&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Branch hochladen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
git push -u origin feature-benutzerprofil&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann auf einer Plattform wie GitHub oder GitLab ein Pull Request beziehungsweise Merge Request erstellt werden.&lt;br /&gt;
&lt;br /&gt;
== Abgrenzung zu anderen Systemen ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! System&lt;br /&gt;
! Typ&lt;br /&gt;
! Besonderheit&lt;br /&gt;
|-&lt;br /&gt;
| Git&lt;br /&gt;
| Verteilte Versionsverwaltung&lt;br /&gt;
| Sehr schnelle Branches und lokale Historie&lt;br /&gt;
|-&lt;br /&gt;
| SVN&lt;br /&gt;
| Zentrale Versionsverwaltung&lt;br /&gt;
| Einfacheres zentrales Modell&lt;br /&gt;
|-&lt;br /&gt;
| Mercurial&lt;br /&gt;
| Verteilte Versionsverwaltung&lt;br /&gt;
| Ähnlich wie Git, aber weniger verbreitet&lt;br /&gt;
|-&lt;br /&gt;
| CVS&lt;br /&gt;
| Zentrale Versionsverwaltung&lt;br /&gt;
| Älteres System&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
Git ist ein leistungsfähiges und weit verbreitetes Versionsverwaltungssystem. Es ermöglicht Entwicklern, Änderungen zuverlässig zu speichern, parallele Entwicklungszweige zu verwalten und gemeinsam an Softwareprojekten zu arbeiten.&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Konzepte sind:&lt;br /&gt;
&lt;br /&gt;
* Repository&lt;br /&gt;
* Commit&lt;br /&gt;
* Branch&lt;br /&gt;
* Merge&lt;br /&gt;
* Rebase&lt;br /&gt;
* Remote&lt;br /&gt;
* Push und Pull&lt;br /&gt;
* Staging Area&lt;br /&gt;
&lt;br /&gt;
Durch seine verteilte Architektur, hohe Geschwindigkeit und starke Branch-Unterstützung ist Git heute ein Standardwerkzeug der professionellen Softwareentwicklung.&lt;br /&gt;
&lt;br /&gt;
== Literatur und Weblinks ==&lt;br /&gt;
&lt;br /&gt;
* Offizielle Git-Webseite: https://git-scm.com/&lt;br /&gt;
* Pro Git Book: https://git-scm.com/book/de/v2&lt;br /&gt;
* Git-Dokumentation: https://git-scm.com/docs&lt;br /&gt;
* GitHub Docs: https://docs.github.com/&lt;br /&gt;
* GitLab Docs: https://docs.gitlab.com/&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Versionsverwaltung]]&lt;br /&gt;
* [[Softwareentwicklung]]&lt;br /&gt;
* [[Repository]]&lt;br /&gt;
* [[Branch]]&lt;br /&gt;
* [[Merge]]&lt;br /&gt;
* [[Continuous Integration]]&lt;br /&gt;
* [[GitHub]]&lt;br /&gt;
* [[GitLab]]&lt;br /&gt;
* [[DevOps]]&lt;br /&gt;
* [[Quellcodeverwaltung]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Softwareentwicklung]]&lt;br /&gt;
[[Kategorie:Versionsverwaltung]]&lt;br /&gt;
[[Kategorie:Git]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=202</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=202"/>
		<updated>2026-06-04T10:27:17Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Deployment */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Git]]&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[Lineare Gewichtung]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== KI und Machine Learning==&lt;br /&gt;
* [[Beam Search]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Beam_Search&amp;diff=201</id>
		<title>Beam Search</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Beam_Search&amp;diff=201"/>
		<updated>2026-05-30T11:02:07Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= Beam Search =  &amp;#039;&amp;#039;&amp;#039;Beam Search&amp;#039;&amp;#039;&amp;#039; ist ein heuristisches Suchverfahren aus der Künstlichen Intelligenz, das zur Lösung von Such- und Optimierungsproblemen verwendet wird. Es stellt eine speichereffiziente Variante der Best-First Search dar und kombiniert Merkmale von Breitensuche und heuristischer Suche.  Die Grundidee besteht darin, in jeder Suchebene nur eine begrenzte Anzahl der vielversprechendsten Kandidaten weit…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Beam Search =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beam Search&#039;&#039;&#039; ist ein heuristisches Suchverfahren aus der [[Künstliche Intelligenz|Künstlichen Intelligenz]], das zur Lösung von Such- und Optimierungsproblemen verwendet wird. Es stellt eine speichereffiziente Variante der [[Best-First Search]] dar und kombiniert Merkmale von [[Breitensuche]] und heuristischer Suche.&lt;br /&gt;
&lt;br /&gt;
Die Grundidee besteht darin, in jeder Suchebene nur eine begrenzte Anzahl der vielversprechendsten Kandidaten weiterzuverfolgen. Dadurch wird der Speicherbedarf erheblich reduziert, allerdings besteht die Gefahr, optimale Lösungen zu verwerfen.&lt;br /&gt;
&lt;br /&gt;
Beam Search wird unter anderem in folgenden Bereichen eingesetzt:&lt;br /&gt;
&lt;br /&gt;
* [[Maschinelles Lernen]]&lt;br /&gt;
* [[Natural Language Processing]]&lt;br /&gt;
* [[Spracherkennung]]&lt;br /&gt;
* [[Maschinelle Übersetzung]]&lt;br /&gt;
* [[Robotik]]&lt;br /&gt;
* [[Planungssysteme]]&lt;br /&gt;
* [[Kombinatorische Optimierung]]&lt;br /&gt;
&lt;br /&gt;
== Motivation ==&lt;br /&gt;
&lt;br /&gt;
Viele Suchprobleme besitzen einen sehr großen Suchraum.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Routenplanung&lt;br /&gt;
* Schachprogramme&lt;br /&gt;
* Produktionsplanung&lt;br /&gt;
* Textgenerierung durch Large Language Models&lt;br /&gt;
* Optimierungsprobleme mit vielen Entscheidungsvariablen&lt;br /&gt;
&lt;br /&gt;
Eine vollständige Suche ist häufig nicht praktikabel, da die Anzahl möglicher Zustände exponentiell wächst.&lt;br /&gt;
&lt;br /&gt;
Beam Search reduziert den Suchaufwand, indem nur die besten Kandidaten jeder Ebene betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Beam Search erweitert die aktuell vielversprechendsten Zustände.&lt;br /&gt;
&lt;br /&gt;
Nach jeder Erweiterung werden die erzeugten Nachfolger bewertet.&lt;br /&gt;
&lt;br /&gt;
Anschließend werden nur die besten Zustände beibehalten.&lt;br /&gt;
&lt;br /&gt;
Alle anderen Zustände werden dauerhaft verworfen.&lt;br /&gt;
&lt;br /&gt;
Die maximale Anzahl gespeicherter Zustände wird als &#039;&#039;&#039;Beam Width&#039;&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
== Beam Width ==&lt;br /&gt;
&lt;br /&gt;
Die Beam Width wird häufig mit &#039;&#039;k&#039;&#039; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
k = 5&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann werden in jeder Ebene höchstens fünf Zustände weiterverfolgt.&lt;br /&gt;
&lt;br /&gt;
Je größer &#039;&#039;k&#039;&#039; gewählt wird:&lt;br /&gt;
&lt;br /&gt;
* desto höher die Lösungsqualität&lt;br /&gt;
* desto größer der Speicherbedarf&lt;br /&gt;
* desto höher die Rechenzeit&lt;br /&gt;
&lt;br /&gt;
Je kleiner &#039;&#039;k&#039;&#039; gewählt wird:&lt;br /&gt;
&lt;br /&gt;
* desto schneller die Suche&lt;br /&gt;
* desto höher das Risiko, gute Lösungen zu verwerfen&lt;br /&gt;
&lt;br /&gt;
== Ablauf des Verfahrens ==&lt;br /&gt;
&lt;br /&gt;
=== Schritt 1: Startzustand ===&lt;br /&gt;
&lt;br /&gt;
Die Suche beginnt mit einem Startknoten.&lt;br /&gt;
&lt;br /&gt;
=== Schritt 2: Erweiterung ===&lt;br /&gt;
&lt;br /&gt;
Alle aktuell gespeicherten Zustände werden expandiert.&lt;br /&gt;
&lt;br /&gt;
=== Schritt 3: Bewertung ===&lt;br /&gt;
&lt;br /&gt;
Jeder Nachfolger erhält einen Bewertungswert.&lt;br /&gt;
&lt;br /&gt;
Dieser kann beispielsweise durch:&lt;br /&gt;
&lt;br /&gt;
* Kostenfunktionen&lt;br /&gt;
* Heuristiken&lt;br /&gt;
* Wahrscheinlichkeiten&lt;br /&gt;
&lt;br /&gt;
bestimmt werden.&lt;br /&gt;
&lt;br /&gt;
=== Schritt 4: Auswahl ===&lt;br /&gt;
&lt;br /&gt;
Die Nachfolger werden sortiert.&lt;br /&gt;
&lt;br /&gt;
Nur die besten &#039;&#039;k&#039;&#039; Zustände bleiben erhalten.&lt;br /&gt;
&lt;br /&gt;
=== Schritt 5: Wiederholung ===&lt;br /&gt;
&lt;br /&gt;
Die Schritte werden solange wiederholt, bis:&lt;br /&gt;
&lt;br /&gt;
* eine Lösung gefunden wurde&lt;br /&gt;
* ein Abbruchkriterium erfüllt ist&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, die Beam Width beträgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
k = 2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Startknoten besitzt vier Nachfolger:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Zustand&lt;br /&gt;
! Bewertung&lt;br /&gt;
|-&lt;br /&gt;
| A&lt;br /&gt;
| 10&lt;br /&gt;
|-&lt;br /&gt;
| B&lt;br /&gt;
| 7&lt;br /&gt;
|-&lt;br /&gt;
| C&lt;br /&gt;
| 5&lt;br /&gt;
|-&lt;br /&gt;
| D&lt;br /&gt;
| 2&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es werden nur die beiden besten Zustände weiterverfolgt:&lt;br /&gt;
&lt;br /&gt;
* A&lt;br /&gt;
* B&lt;br /&gt;
&lt;br /&gt;
Die Zustände C und D werden verworfen.&lt;br /&gt;
&lt;br /&gt;
== Pseudocode ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
BeamSearch(Start, k):&lt;br /&gt;
&lt;br /&gt;
Beam ← {Start}&lt;br /&gt;
&lt;br /&gt;
while nicht beendet:&lt;br /&gt;
&lt;br /&gt;
    Kandidaten ← {}&lt;br /&gt;
&lt;br /&gt;
    für jeden Zustand in Beam:&lt;br /&gt;
        Nachfolger erzeugen&lt;br /&gt;
        Kandidaten hinzufügen&lt;br /&gt;
&lt;br /&gt;
    Kandidaten sortieren&lt;br /&gt;
&lt;br /&gt;
    Beam ← beste k Kandidaten&lt;br /&gt;
&lt;br /&gt;
return beste Lösung&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mathematische Beschreibung ==&lt;br /&gt;
&lt;br /&gt;
Sei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
B_t&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die Menge der Zustände im Suchstrahl (Beam) zum Zeitpunkt t.&lt;br /&gt;
&lt;br /&gt;
Nach der Expansion entsteht:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
C_t&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
als Menge aller Nachfolger.&lt;br /&gt;
&lt;br /&gt;
Anschließend wird ausgewählt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
B_{t+1}&lt;br /&gt;
=&lt;br /&gt;
Top_k(C_t)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wobei:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Top_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die Auswahl der k besten Zustände beschreibt.&lt;br /&gt;
&lt;br /&gt;
== Eigenschaften ==&lt;br /&gt;
&lt;br /&gt;
=== Unvollständigkeit ===&lt;br /&gt;
&lt;br /&gt;
Beam Search ist nicht vollständig.&lt;br /&gt;
&lt;br /&gt;
Eine existierende Lösung kann verworfen werden.&lt;br /&gt;
&lt;br /&gt;
=== Keine Optimalitätsgarantie ===&lt;br /&gt;
&lt;br /&gt;
Das Verfahren garantiert nicht das Auffinden der optimalen Lösung.&lt;br /&gt;
&lt;br /&gt;
=== Speicherbegrenzung ===&lt;br /&gt;
&lt;br /&gt;
Der Speicherbedarf bleibt kontrollierbar.&lt;br /&gt;
&lt;br /&gt;
=== Hohe Geschwindigkeit ===&lt;br /&gt;
&lt;br /&gt;
Beam Search ist deutlich schneller als vollständige Suchverfahren.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Suchverfahren ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Vollständig&lt;br /&gt;
! Optimal&lt;br /&gt;
! Speicherbedarf&lt;br /&gt;
|-&lt;br /&gt;
| Breitensuche&lt;br /&gt;
| Ja&lt;br /&gt;
| Ja&lt;br /&gt;
| Sehr hoch&lt;br /&gt;
|-&lt;br /&gt;
| Tiefensuche&lt;br /&gt;
| Nein&lt;br /&gt;
| Nein&lt;br /&gt;
| Gering&lt;br /&gt;
|-&lt;br /&gt;
| A*&lt;br /&gt;
| Ja&lt;br /&gt;
| Ja&lt;br /&gt;
| Hoch&lt;br /&gt;
|-&lt;br /&gt;
| Beam Search&lt;br /&gt;
| Nein&lt;br /&gt;
| Nein&lt;br /&gt;
| Kontrollierbar&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Beam Search und A* ==&lt;br /&gt;
&lt;br /&gt;
Beam Search wird häufig als speicherbegrenzte Variante von [[A*-Algorithmus|A*]] betrachtet.&lt;br /&gt;
&lt;br /&gt;
Während A* alle vielversprechenden Zustände speichert, begrenzt Beam Search deren Anzahl.&lt;br /&gt;
&lt;br /&gt;
Dadurch wird Speicher eingespart.&lt;br /&gt;
&lt;br /&gt;
Allerdings gehen Optimalitätsgarantien verloren.&lt;br /&gt;
&lt;br /&gt;
== Einfluss der Beam Width ==&lt;br /&gt;
&lt;br /&gt;
=== Kleine Beam Width ===&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
k = 1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies entspricht einer Greedy Search.&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&lt;br /&gt;
* Sehr schnell&lt;br /&gt;
* Sehr geringer Speicherbedarf&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Schlechte Lösungsqualität&lt;br /&gt;
&lt;br /&gt;
=== Große Beam Width ===&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
k = 100&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Vorteile:&lt;br /&gt;
&lt;br /&gt;
* Höhere Wahrscheinlichkeit guter Lösungen&lt;br /&gt;
&lt;br /&gt;
Nachteile:&lt;br /&gt;
&lt;br /&gt;
* Höherer Ressourcenverbrauch&lt;br /&gt;
&lt;br /&gt;
== Anwendungen in der Künstlichen Intelligenz ==&lt;br /&gt;
&lt;br /&gt;
=== Maschinelle Übersetzung ===&lt;br /&gt;
&lt;br /&gt;
Neuronale Übersetzungsmodelle erzeugen für jedes Wort mehrere mögliche Fortsetzungen.&lt;br /&gt;
&lt;br /&gt;
Beam Search behält die wahrscheinlichsten Übersetzungen.&lt;br /&gt;
&lt;br /&gt;
=== Sprachmodelle ===&lt;br /&gt;
&lt;br /&gt;
Viele Large Language Models verwenden Beam Search zur Textgenerierung.&lt;br /&gt;
&lt;br /&gt;
Dabei werden mehrere mögliche Satzfortsetzungen parallel betrachtet.&lt;br /&gt;
&lt;br /&gt;
=== Spracherkennung ===&lt;br /&gt;
&lt;br /&gt;
Mehrere mögliche Wortfolgen werden bewertet und verglichen.&lt;br /&gt;
&lt;br /&gt;
=== Bildbeschreibung ===&lt;br /&gt;
&lt;br /&gt;
KI-Systeme erzeugen mehrere Kandidatensätze für Bildbeschreibungen.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen in der Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Beam Search wird häufig für kombinatorische Optimierungsprobleme eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Produktionsplanung&lt;br /&gt;
* Tourenplanung&lt;br /&gt;
* Ressourcenallokation&lt;br /&gt;
* Scheduling&lt;br /&gt;
* Netzwerkoptimierung&lt;br /&gt;
&lt;br /&gt;
== Beispiel aus der Produktionsplanung ==&lt;br /&gt;
&lt;br /&gt;
Eine Fabrik möchte eine optimale Produktionsreihenfolge bestimmen.&lt;br /&gt;
&lt;br /&gt;
Jede Entscheidung erzeugt neue Teilpläne.&lt;br /&gt;
&lt;br /&gt;
Eine vollständige Suche wäre zu teuer.&lt;br /&gt;
&lt;br /&gt;
Beam Search verfolgt nur die vielversprechendsten Teilpläne weiter.&lt;br /&gt;
&lt;br /&gt;
Dadurch können gute Lösungen in akzeptabler Zeit gefunden werden.&lt;br /&gt;
&lt;br /&gt;
== Beam Search in der Multi-Objective-Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Bei Mehrzielproblemen kann die Bewertung eines Zustands beispielsweise erfolgen durch:&lt;br /&gt;
&lt;br /&gt;
* gewichtete Summe der Zielfunktionen&lt;br /&gt;
* Pareto-Rang&lt;br /&gt;
* Dominanzbeziehungen&lt;br /&gt;
&lt;br /&gt;
Beam Search dient dabei häufig als Metaheuristik zur Approximation guter Lösungen.&lt;br /&gt;
&lt;br /&gt;
== Varianten ==&lt;br /&gt;
&lt;br /&gt;
=== Stochastic Beam Search ===&lt;br /&gt;
&lt;br /&gt;
Die Auswahl erfolgt zufallsbasiert entsprechend einer Wahrscheinlichkeitsverteilung.&lt;br /&gt;
&lt;br /&gt;
Dadurch wird die Diversität erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Local Beam Search ===&lt;br /&gt;
&lt;br /&gt;
Mehrere lokale Suchprozesse laufen parallel.&lt;br /&gt;
&lt;br /&gt;
Die besten Zustände werden gemeinsam betrachtet.&lt;br /&gt;
&lt;br /&gt;
=== Diverse Beam Search ===&lt;br /&gt;
&lt;br /&gt;
Zusätzlich wird die Vielfalt der Kandidaten berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
Dies wird häufig bei Sprachmodellen verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Beam Stack Search ===&lt;br /&gt;
&lt;br /&gt;
Kombination aus Beam Search und Backtracking.&lt;br /&gt;
&lt;br /&gt;
Dadurch können zuvor verworfene Bereiche erneut untersucht werden.&lt;br /&gt;
&lt;br /&gt;
== Vor- und Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Vorteile ===&lt;br /&gt;
&lt;br /&gt;
* Einfach zu implementieren&lt;br /&gt;
* Geringer Speicherbedarf&lt;br /&gt;
* Gute Skalierbarkeit&lt;br /&gt;
* Schnelle Laufzeiten&lt;br /&gt;
* Für große Suchräume geeignet&lt;br /&gt;
&lt;br /&gt;
=== Nachteile ===&lt;br /&gt;
&lt;br /&gt;
* Keine Optimalitätsgarantie&lt;br /&gt;
* Nicht vollständig&lt;br /&gt;
* Empfindlich gegenüber der Wahl der Beam Width&lt;br /&gt;
* Gute Lösungen können früh verworfen werden&lt;br /&gt;
&lt;br /&gt;
== Komplexität ==&lt;br /&gt;
&lt;br /&gt;
Seien:&lt;br /&gt;
&lt;br /&gt;
* b = Verzweigungsfaktor&lt;br /&gt;
* d = Suchtiefe&lt;br /&gt;
* k = Beam Width&lt;br /&gt;
&lt;br /&gt;
Dann ergibt sich näherungsweise:&lt;br /&gt;
&lt;br /&gt;
Zeitaufwand:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
O(k \cdot b \cdot d)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Speicherbedarf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
O(k)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Speicherbedarf wächst somit linear mit der Beam Width.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
&lt;br /&gt;
* Russell, S.; Norvig, P.: &#039;&#039;Artificial Intelligence: A Modern Approach&#039;&#039;. Pearson.&lt;br /&gt;
* Pearl, J.: &#039;&#039;Heuristics: Intelligent Search Strategies for Computer Problem Solving&#039;&#039;. Addison-Wesley.&lt;br /&gt;
* Jurafsky, D.; Martin, J.: &#039;&#039;Speech and Language Processing&#039;&#039;. Pearson.&lt;br /&gt;
* Koehler, G.: &#039;&#039;Künstliche Intelligenz&#039;&#039;. Springer Vieweg.&lt;br /&gt;
* Ghallab, M.; Nau, D.; Traverso, P.: &#039;&#039;Automated Planning&#039;&#039;. Morgan Kaufmann.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Künstliche Intelligenz]]&lt;br /&gt;
* [[Heuristik]]&lt;br /&gt;
* [[Breitensuche]]&lt;br /&gt;
* [[Tiefensuche]]&lt;br /&gt;
* [[A*-Algorithmus]]&lt;br /&gt;
* [[Best-First Search]]&lt;br /&gt;
* [[Metaheuristik]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Genetischer Algorithmus]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=200</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=200"/>
		<updated>2026-05-30T11:01:08Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Optimierungsverfahren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[Lineare Gewichtung]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== KI und Machine Learning==&lt;br /&gt;
* [[Beam Search]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Lineare_Gewichtung&amp;diff=199</id>
		<title>Lineare Gewichtung</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Lineare_Gewichtung&amp;diff=199"/>
		<updated>2026-05-19T13:41:15Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= Lineare Gewichtung =  Die &amp;#039;&amp;#039;&amp;#039;lineare Gewichtung&amp;#039;&amp;#039;&amp;#039; (englisch: &amp;#039;&amp;#039;Weighted Sum Method&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;Linear Weighting Method&amp;#039;&amp;#039;) ist ein Verfahren der Mehrzieloptimierung, bei dem mehrere Zielfunktionen zu einer einzigen Zielfunktion zusammengefasst werden.  Dabei wird jeder Zielfunktion ein Gewicht zugeordnet, das ihre relative Bedeutung beschreibt. Die gewichteten Zielfunktionen werden anschließend addiert.  Die lineare Gewich…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Lineare Gewichtung =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;lineare Gewichtung&#039;&#039;&#039; (englisch: &#039;&#039;Weighted Sum Method&#039;&#039; oder &#039;&#039;Linear Weighting Method&#039;&#039;) ist ein Verfahren der [[Multi-Objective Optimization|Mehrzieloptimierung]], bei dem mehrere Zielfunktionen zu einer einzigen Zielfunktion zusammengefasst werden.&lt;br /&gt;
&lt;br /&gt;
Dabei wird jeder Zielfunktion ein Gewicht zugeordnet, das ihre relative Bedeutung beschreibt. Die gewichteten Zielfunktionen werden anschließend addiert.&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung gehört zu den einfachsten und am häufigsten verwendeten Verfahren der Mehrzieloptimierung.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Viele Optimierungsprobleme besitzen mehrere konkurrierende Zielgrößen.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Minimierung von Kosten&lt;br /&gt;
* Minimierung von Emissionen&lt;br /&gt;
* Maximierung der Qualität&lt;br /&gt;
* Minimierung der Laufzeit&lt;br /&gt;
&lt;br /&gt;
Da klassische Optimierungsverfahren meist nur eine Zielfunktion verarbeiten können, werden die einzelnen Ziele zu einer gemeinsamen Zielfunktion kombiniert.&lt;br /&gt;
&lt;br /&gt;
Dies geschieht durch Gewichtungsfaktoren.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Formulierung ==&lt;br /&gt;
&lt;br /&gt;
Ein allgemeines Mehrzielproblem lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; (f_1(x), f_2(x), ..., f_k(x))&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der linearen Gewichtung wird daraus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Z(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Z(x) = \sum_{i=1}^{k} w_i f_i(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;f_i(x)&amp;lt;/math&amp;gt; = Zielfunktionen&lt;br /&gt;
* &amp;lt;math&amp;gt;w_i&amp;lt;/math&amp;gt; = Gewichtungsfaktoren&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; = Entscheidungsvariablen&lt;br /&gt;
&lt;br /&gt;
Die Gewichte erfüllen typischerweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
w_i \geq 0&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\sum_{i=1}^{k} w_i = 1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bedeutung der Gewichte ==&lt;br /&gt;
&lt;br /&gt;
Die Gewichte bestimmen die relative Wichtigkeit der einzelnen Ziele.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Ziel&lt;br /&gt;
! Gewicht&lt;br /&gt;
|-&lt;br /&gt;
| Kostenminimierung&lt;br /&gt;
| 0,7&lt;br /&gt;
|-&lt;br /&gt;
| Emissionsminimierung&lt;br /&gt;
| 0,3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel besitzt die Kostenminimierung eine höhere Priorität.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Ein Unternehmen möchte:&lt;br /&gt;
&lt;br /&gt;
* Produktionskosten minimieren&lt;br /&gt;
* Energieverbrauch minimieren&lt;br /&gt;
&lt;br /&gt;
Die Zielfunktionen lauten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_1(x) = Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) = Energieverbrauch(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gewichtete Zielfunktion lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Z(x) = 0.8 \cdot Kosten(x) + 0.2 \cdot Energieverbrauch(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird ein einzelnes Optimierungsproblem erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Vorgehensweise ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Definition der Zielfunktionen ===&lt;br /&gt;
&lt;br /&gt;
Zunächst werden alle relevanten Zielgrößen formuliert.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Kosten&lt;br /&gt;
* Qualität&lt;br /&gt;
* Energieverbrauch&lt;br /&gt;
* Lieferzeit&lt;br /&gt;
&lt;br /&gt;
=== 2. Wahl der Gewichte ===&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jede Zielfunktion ein Gewicht festgelegt.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte spiegeln die Präferenzen des Entscheiders wider.&lt;br /&gt;
&lt;br /&gt;
=== 3. Bildung der Gesamtzielfunktion ===&lt;br /&gt;
&lt;br /&gt;
Die gewichteten Ziele werden addiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Z(x) = w_1 f_1(x) + w_2 f_2(x) + ... + w_k f_k(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. Lösung des Optimierungsproblems ===&lt;br /&gt;
&lt;br /&gt;
Das resultierende Einzelzielproblem wird mit klassischen Verfahren gelöst:&lt;br /&gt;
&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Nichtlineare Optimierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
&lt;br /&gt;
== Normierung der Zielfunktionen ==&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Zielfunktionen besitzen häufig unterschiedliche Größenordnungen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Kosten in Euro&lt;br /&gt;
* Emissionen in Kilogramm&lt;br /&gt;
* Zeit in Sekunden&lt;br /&gt;
&lt;br /&gt;
Ohne Normierung könnten große Zahlenbereiche dominante Effekte erzeugen.&lt;br /&gt;
&lt;br /&gt;
Daher werden Zielfunktionen häufig normiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_i^{norm}(x) = \frac{f_i(x) - f_i^{min}}{f_i^{max} - f_i^{min}}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gewichtete Zielfunktion verwendet dann die normierten Werte.&lt;br /&gt;
&lt;br /&gt;
== Eigenschaften ==&lt;br /&gt;
&lt;br /&gt;
=== Einfachheit ===&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung ist mathematisch einfach und leicht implementierbar.&lt;br /&gt;
&lt;br /&gt;
=== Gute Solver-Unterstützung ===&lt;br /&gt;
&lt;br /&gt;
Da nur eine Zielfunktion entsteht, können Standard-Solver verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Direkte Berücksichtigung von Präferenzen ===&lt;br /&gt;
&lt;br /&gt;
Gewichte ermöglichen eine direkte Priorisierung der Ziele.&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Schwierige Wahl geeigneter Gewichte ===&lt;br /&gt;
&lt;br /&gt;
Die Auswahl sinnvoller Gewichte ist oft problematisch.&lt;br /&gt;
&lt;br /&gt;
Kleine Änderungen der Gewichte können große Auswirkungen auf die Lösung haben.&lt;br /&gt;
&lt;br /&gt;
=== Unterschiedliche Skalierungen ===&lt;br /&gt;
&lt;br /&gt;
Ohne Normierung dominieren Ziele mit großen Zahlenwerten.&lt;br /&gt;
&lt;br /&gt;
=== Probleme bei nicht-konvexen Pareto-Fronten ===&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung kann bestimmte Pareto-Lösungen nicht finden.&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei nicht-konvexen Pareto-Fronten entstehen Lücken.&lt;br /&gt;
&lt;br /&gt;
=== Keine vollständige Pareto-Abdeckung ===&lt;br /&gt;
&lt;br /&gt;
Nicht alle Pareto-optimalen Lösungen sind mit der gewichteten Summe erreichbar.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang mit Pareto-Optimalität ==&lt;br /&gt;
&lt;br /&gt;
Jede Lösung der gewichteten Summe ist unter bestimmten Bedingungen Pareto-optimal.&lt;br /&gt;
&lt;br /&gt;
Allerdings gilt dies nur vollständig bei konvexen Pareto-Fronten.&lt;br /&gt;
&lt;br /&gt;
Nicht-konvexe Bereiche können durch die lineare Gewichtung nicht erreicht werden.&lt;br /&gt;
&lt;br /&gt;
== Geometrische Interpretation ==&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung kann geometrisch als Verschiebung einer Hyperebene interpretiert werden.&lt;br /&gt;
&lt;br /&gt;
Die Gewichte bestimmen die Steigung dieser Hyperebene.&lt;br /&gt;
&lt;br /&gt;
Die optimale Lösung liegt dort, wo die Hyperebene den zulässigen Lösungsraum zuletzt berührt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel einer Pareto-Front ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Gewichtung&lt;br /&gt;
! Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;w_1 = 0.9&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;w_2 = 0.1&amp;lt;/math&amp;gt;&lt;br /&gt;
| Kosten stark priorisiert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;w_1 = 0.5&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;w_2 = 0.5&amp;lt;/math&amp;gt;&lt;br /&gt;
| Ausgewogene Lösung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;w_1 = 0.2&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;w_2 = 0.8&amp;lt;/math&amp;gt;&lt;br /&gt;
| Umweltwirkung stark priorisiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung wird in vielen Bereichen eingesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Produktion ===&lt;br /&gt;
&lt;br /&gt;
* Kosten vs. Qualität&lt;br /&gt;
* Produktionsmenge vs. Energieverbrauch&lt;br /&gt;
&lt;br /&gt;
=== Logistik ===&lt;br /&gt;
&lt;br /&gt;
* Lieferzeit vs. Transportkosten&lt;br /&gt;
* Emissionen vs. Effizienz&lt;br /&gt;
&lt;br /&gt;
=== Energietechnik ===&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit vs. CO₂-Ausstoß&lt;br /&gt;
* Netzstabilität vs. Kosten&lt;br /&gt;
&lt;br /&gt;
=== Softwareentwicklung ===&lt;br /&gt;
&lt;br /&gt;
* Laufzeit vs. Speicherverbrauch&lt;br /&gt;
* Performance vs. Energieeffizienz&lt;br /&gt;
* Antwortzeit vs. Hardwarekosten&lt;br /&gt;
&lt;br /&gt;
=== Landwirtschaft ===&lt;br /&gt;
&lt;br /&gt;
* Kostenminimierung&lt;br /&gt;
* Minimierung von Nährstoffabweichungen&lt;br /&gt;
* Minimierung von Umweltbelastungen&lt;br /&gt;
&lt;br /&gt;
== Beispiel in der Düngemitteloptimierung ==&lt;br /&gt;
&lt;br /&gt;
In einem Düngemittel-Empfehlungssystem könnte folgende gewichtete Zielfunktion verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Z(x) =&lt;br /&gt;
0.5 \cdot Kosten(x)&lt;br /&gt;
+&lt;br /&gt;
0.3 \cdot A_N(x)&lt;br /&gt;
+&lt;br /&gt;
0.2 \cdot N_U(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei beschreibt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;Kosten(x)&amp;lt;/math&amp;gt; die Gesamtkosten&lt;br /&gt;
* &amp;lt;math&amp;gt;A_N(x)&amp;lt;/math&amp;gt; die Nährstoffabweichung&lt;br /&gt;
* &amp;lt;math&amp;gt;N_U(x)&amp;lt;/math&amp;gt; den Stickstoffüberschuss&lt;br /&gt;
&lt;br /&gt;
Durch Veränderung der Gewichte können unterschiedliche Strategien erzeugt werden.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Verfahren ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Grundidee&lt;br /&gt;
! Vorteile&lt;br /&gt;
! Nachteile&lt;br /&gt;
|-&lt;br /&gt;
| Lineare Gewichtung&lt;br /&gt;
| Gewichtete Summe aller Ziele&lt;br /&gt;
| Einfach und effizient&lt;br /&gt;
| Probleme bei nicht-konvexen Fronten&lt;br /&gt;
|-&lt;br /&gt;
| [[ε-Constraint-Methode]]&lt;br /&gt;
| Ein Ziel optimieren, andere begrenzen&lt;br /&gt;
| Gute Pareto-Abdeckung&lt;br /&gt;
| Mehrfache Optimierung erforderlich&lt;br /&gt;
|-&lt;br /&gt;
| Lexikographische Optimierung&lt;br /&gt;
| Ziele nach Priorität ordnen&lt;br /&gt;
| Klare Hierarchie&lt;br /&gt;
| Niedrige Flexibilität&lt;br /&gt;
|-&lt;br /&gt;
| [[NSGA-II]]&lt;br /&gt;
| Evolutionäre Pareto-Suche&lt;br /&gt;
| Viele Lösungen gleichzeitig&lt;br /&gt;
| Hoher Rechenaufwand&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Erweiterte Varianten ==&lt;br /&gt;
&lt;br /&gt;
=== Adaptive Gewichtung ===&lt;br /&gt;
&lt;br /&gt;
Die Gewichte werden während der Optimierung dynamisch angepasst.&lt;br /&gt;
&lt;br /&gt;
=== Nichtlineare Gewichtung ===&lt;br /&gt;
&lt;br /&gt;
Die Zielfunktionen werden nichtlinear kombiniert.&lt;br /&gt;
&lt;br /&gt;
=== Goal Programming ===&lt;br /&gt;
&lt;br /&gt;
Abweichungen von Zielwerten werden minimiert.&lt;br /&gt;
&lt;br /&gt;
=== Interaktive Gewichtung ===&lt;br /&gt;
&lt;br /&gt;
Der Entscheider verändert die Gewichte während der Optimierung.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung in der Multi-Objective-Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Die lineare Gewichtung zählt zu den wichtigsten klassischen Verfahren der Mehrzieloptimierung.&lt;br /&gt;
&lt;br /&gt;
Sie wird häufig eingesetzt:&lt;br /&gt;
&lt;br /&gt;
* wegen ihrer Einfachheit&lt;br /&gt;
* wegen der guten mathematischen Eigenschaften&lt;br /&gt;
* wegen der hohen Solver-Kompatibilität&lt;br /&gt;
&lt;br /&gt;
Trotz ihrer Einschränkungen dient sie oft als Ausgangspunkt für komplexere Verfahren.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
&lt;br /&gt;
* Ehrgott, M.: &#039;&#039;Multicriteria Optimization&#039;&#039;. Springer, 2005.&lt;br /&gt;
* Miettinen, K.: &#039;&#039;Nonlinear Multiobjective Optimization&#039;&#039;. Springer, 1999.&lt;br /&gt;
* Deb, K.: &#039;&#039;Multi-Objective Optimization using Evolutionary Algorithms&#039;&#039;. Wiley, 2001.&lt;br /&gt;
* Steuer, R. E.: &#039;&#039;Multiple Criteria Optimization: Theory, Computation and Application&#039;&#039;. Wiley, 1986.&lt;br /&gt;
* Marler, R. T.; Arora, J. S.: &#039;&#039;Survey of Multi-Objective Optimization Methods for Engineering&#039;&#039;, Structural and Multidisciplinary Optimization, 2004.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* [[ε-Constraint-Methode]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Goal Programming]]&lt;br /&gt;
* [[Lexikographische Optimierung]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=198</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=198"/>
		<updated>2026-05-19T13:40:53Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Optimierungsverfahren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[Lineare Gewichtung]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=197</id>
		<title>Ε-Constraint-Methode</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=197"/>
		<updated>2026-05-19T13:36:10Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;```mediawiki&lt;br /&gt;
= ε-Constraint-Methode =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;ε-Constraint-Methode&#039;&#039;&#039; (auch &#039;&#039;Epsilon-Constraint-Methode&#039;&#039;) ist ein Verfahren der [[Multi-Objective Optimization|Mehrzieloptimierung]], bei dem eine Zielfunktion optimiert wird, während die übrigen Zielfunktionen als Nebenbedingungen formuliert werden.&lt;br /&gt;
&lt;br /&gt;
Die Methode gehört zu den klassischen Verfahren zur Berechnung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]] und wird insbesondere in der linearen, nichtlinearen und kombinatorischen Optimierung eingesetzt.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Bei vielen Optimierungsproblemen existieren mehrere Zielgrößen gleichzeitig. Diese Ziele stehen häufig in Konkurrenz zueinander.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Minimierung der Kosten&lt;br /&gt;
* Minimierung von Emissionen&lt;br /&gt;
* Maximierung der Qualität&lt;br /&gt;
* Minimierung der Bearbeitungszeit&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wählt eines dieser Ziele als primäre Zielfunktion aus. Alle anderen Ziele werden durch Grenzwerte (ε-Werte) eingeschränkt.&lt;br /&gt;
&lt;br /&gt;
Dadurch wird aus einem Mehrzielproblem ein klassisches Einzelzielproblem.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Formulierung ==&lt;br /&gt;
&lt;br /&gt;
Ein allgemeines Multi-Objective-Problem lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; (f_1(x), f_2(x), ..., f_k(x))&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in S&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;f_i(x)&amp;lt;/math&amp;gt; = Zielfunktionen&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; = Entscheidungsvariablen&lt;br /&gt;
* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; = zulässiger Lösungsraum&lt;br /&gt;
&lt;br /&gt;
=== Umformung mit der ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird ausgewählt, beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; f_1(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die übrigen Zielfunktionen werden in Nebenbedingungen umgewandelt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_3(x) \leq \varepsilon_3&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_k(x) \leq \varepsilon_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch entsteht ein gewöhnliches Optimierungsproblem.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung des ε-Wertes ==&lt;br /&gt;
&lt;br /&gt;
Der Parameter ε definiert einen maximal zulässigen Wert für eine Zielfunktion.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Kosten sollen minimiert werden&lt;br /&gt;
* CO₂-Ausstoß darf höchstens 100 kg betragen&lt;br /&gt;
&lt;br /&gt;
Dann lautet die Nebenbedingung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
CO_2(x) \leq 100&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Variation von ε können unterschiedliche Pareto-Lösungen erzeugt werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang mit Pareto-Optimalität ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode dient vor allem zur Erzeugung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]].&lt;br /&gt;
&lt;br /&gt;
Wird der ε-Wert systematisch verändert, entstehen verschiedene Kompromisslösungen zwischen den Zielgrößen. Dadurch lässt sich die sogenannte Pareto-Front approximieren.&lt;br /&gt;
&lt;br /&gt;
== Vorgehensweise ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Auswahl der Hauptzielfunktion ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird als primäres Optimierungsziel festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. Definition der ε-Grenzen ===&lt;br /&gt;
&lt;br /&gt;
Für die übrigen Ziele werden zulässige Grenzwerte festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
E(x) \leq 50&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
A_N(x) \leq 10&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei beschreibt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;E(x)&amp;lt;/math&amp;gt; die Emissionen&lt;br /&gt;
* &amp;lt;math&amp;gt;A_N(x)&amp;lt;/math&amp;gt; die Nährstoffabweichung&lt;br /&gt;
&lt;br /&gt;
=== 3. Lösung des Einzelzielproblems ===&lt;br /&gt;
&lt;br /&gt;
Das resultierende Problem wird mit klassischen Verfahren gelöst:&lt;br /&gt;
&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
&lt;br /&gt;
=== 4. Variation der ε-Werte ===&lt;br /&gt;
&lt;br /&gt;
Durch wiederholtes Lösen mit unterschiedlichen ε-Werten werden weitere Pareto-Lösungen erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Ein Unternehmen möchte:&lt;br /&gt;
&lt;br /&gt;
* Produktionskosten minimieren&lt;br /&gt;
* Energieverbrauch begrenzen&lt;br /&gt;
&lt;br /&gt;
=== Zielfunktionen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_1(x) = Kosten&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) = Energieverbrauch&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ε-Constraint-Formulierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Energieverbrauch(x) \leq \varepsilon&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun wird ε schrittweise verändert:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ε-Wert&lt;br /&gt;
! Ergebnis&lt;br /&gt;
|-&lt;br /&gt;
| 100&lt;br /&gt;
| Sehr günstige Lösung, hoher Energieverbrauch erlaubt&lt;br /&gt;
|-&lt;br /&gt;
| 80&lt;br /&gt;
| Ausgewogenere Lösung&lt;br /&gt;
|-&lt;br /&gt;
| 50&lt;br /&gt;
| Energieeffiziente, aber teurere Lösung&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Vorteile ==&lt;br /&gt;
&lt;br /&gt;
=== Gute Kontrolle über Nebenbedingungen ===&lt;br /&gt;
&lt;br /&gt;
Der Entscheider kann konkrete Grenzwerte vorgeben.&lt;br /&gt;
&lt;br /&gt;
=== Erzeugung echter Pareto-Lösungen ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann Pareto-optimale Lösungen systematisch erzeugen.&lt;br /&gt;
&lt;br /&gt;
=== Kompatibel mit klassischen Solvern ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann mit vorhandenen LP-, MILP- oder NLP-Solvern verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Besonders geeignet für diskrete Probleme ===&lt;br /&gt;
&lt;br /&gt;
Die Methode funktioniert oft besser als gewichtete Summenverfahren bei nicht-konvexen Pareto-Fronten.&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Wahl geeigneter ε-Werte schwierig ===&lt;br /&gt;
&lt;br /&gt;
Die Qualität der Ergebnisse hängt stark von den gewählten Grenzwerten ab.&lt;br /&gt;
&lt;br /&gt;
=== Hoher Rechenaufwand ===&lt;br /&gt;
&lt;br /&gt;
Für viele ε-Werte muss das Optimierungsproblem mehrfach gelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Möglicherweise unzulässige Probleme ===&lt;br /&gt;
&lt;br /&gt;
Zu strenge ε-Werte können dazu führen, dass keine zulässige Lösung existiert.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Verfahren ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Grundidee&lt;br /&gt;
! Vorteile&lt;br /&gt;
! Nachteile&lt;br /&gt;
|-&lt;br /&gt;
| Gewichtete Summe&lt;br /&gt;
| Kombination aller Ziele zu einer Funktion&lt;br /&gt;
| Einfach umzusetzen&lt;br /&gt;
| Probleme bei nicht-konvexen Pareto-Fronten&lt;br /&gt;
|-&lt;br /&gt;
| ε-Constraint-Methode&lt;br /&gt;
| Ein Ziel optimieren, andere begrenzen&lt;br /&gt;
| Gute Pareto-Abdeckung&lt;br /&gt;
| Mehrfache Optimierung notwendig&lt;br /&gt;
|-&lt;br /&gt;
| Lexikographische Optimierung&lt;br /&gt;
| Ziele nach Priorität ordnen&lt;br /&gt;
| Klare Priorisierung&lt;br /&gt;
| Niedrig priorisierte Ziele werden kaum berücksichtigt&lt;br /&gt;
|-&lt;br /&gt;
| Evolutionäre Verfahren&lt;br /&gt;
| Population von Lösungen erzeugen&lt;br /&gt;
| Viele Pareto-Lösungen gleichzeitig&lt;br /&gt;
| Hoher Rechenaufwand&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Erweiterte Varianten ==&lt;br /&gt;
&lt;br /&gt;
=== Adaptive ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Die ε-Werte werden dynamisch angepasst, um die Pareto-Front gleichmäßiger abzutasten.&lt;br /&gt;
&lt;br /&gt;
=== Augmented ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Zusätzliche Terme verhindern schwach Pareto-optimale Lösungen.&lt;br /&gt;
&lt;br /&gt;
=== Hybridverfahren ===&lt;br /&gt;
&lt;br /&gt;
Kombination mit:&lt;br /&gt;
&lt;br /&gt;
* [[Genetischer Algorithmus]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Simulated Annealing]]&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wird in vielen Bereichen eingesetzt.&lt;br /&gt;
&lt;br /&gt;
=== Produktion ===&lt;br /&gt;
&lt;br /&gt;
* Kosten vs. Qualität&lt;br /&gt;
* Energieverbrauch vs. Produktionsmenge&lt;br /&gt;
&lt;br /&gt;
=== Logistik ===&lt;br /&gt;
&lt;br /&gt;
* Transportkosten vs. Lieferzeit&lt;br /&gt;
* Emissionen vs. Effizienz&lt;br /&gt;
&lt;br /&gt;
=== Landwirtschaft ===&lt;br /&gt;
&lt;br /&gt;
* Kostenminimierung&lt;br /&gt;
* Minimierung von Stickstoffüberschüssen&lt;br /&gt;
* Minimierung von Nährstoffabweichungen&lt;br /&gt;
&lt;br /&gt;
=== Energietechnik ===&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit vs. CO₂-Ausstoß&lt;br /&gt;
* Netzstabilität vs. Kosten&lt;br /&gt;
&lt;br /&gt;
=== Softwareentwicklung ===&lt;br /&gt;
&lt;br /&gt;
* Laufzeit vs. Speicherverbrauch&lt;br /&gt;
* Performance vs. Energieeffizienz&lt;br /&gt;
&lt;br /&gt;
== Beispiel in der Düngemitteloptimierung ==&lt;br /&gt;
&lt;br /&gt;
In einem Düngemittel-Empfehlungssystem könnte folgende Zielfunktion minimiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
A_N(x) \leq \varepsilon_1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
N_U(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei beschreibt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;A_N(x)&amp;lt;/math&amp;gt; die Nährstoffabweichung&lt;br /&gt;
* &amp;lt;math&amp;gt;N_U(x)&amp;lt;/math&amp;gt; den Stickstoffüberschuss&lt;br /&gt;
&lt;br /&gt;
Durch Variation von &amp;lt;math&amp;gt;\varepsilon_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\varepsilon_2&amp;lt;/math&amp;gt; entstehen unterschiedliche Kompromisse zwischen:&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit&lt;br /&gt;
* Bedarfsdeckung&lt;br /&gt;
* Umweltwirkung&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang zur Pareto-Front ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode ist besonders wichtig, weil sie auch nicht-konvexe Bereiche der Pareto-Front finden kann.&lt;br /&gt;
&lt;br /&gt;
Gewichtete Summenverfahren scheitern häufig daran, solche Lösungen zu entdecken.&lt;br /&gt;
&lt;br /&gt;
Dadurch gilt die ε-Constraint-Methode als eines der wichtigsten klassischen Verfahren der Multi-Objective-Optimierung.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
&lt;br /&gt;
* Miettinen, K.: &#039;&#039;Nonlinear Multiobjective Optimization&#039;&#039;. Springer, 1999.&lt;br /&gt;
* Ehrgott, M.: &#039;&#039;Multicriteria Optimization&#039;&#039;. Springer, 2005.&lt;br /&gt;
* Deb, K.: &#039;&#039;Multi-Objective Optimization using Evolutionary Algorithms&#039;&#039;. Wiley, 2001.&lt;br /&gt;
* Steuer, R. E.: &#039;&#039;Multiple Criteria Optimization: Theory, Computation and Application&#039;&#039;. Wiley, 1986.&lt;br /&gt;
* Haimes, Y. Y.; Lasdon, L. S.; Wismer, D. A.: &#039;&#039;On a Bicriterion Formulation of the Problems of Integrated System Identification and System Optimization&#039;&#039;, IEEE Transactions on Systems, Man, and Cybernetics, 1971.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
* [[Lexikographische Optimierung]]&lt;br /&gt;
* [[Gewichtete Summe]]&lt;br /&gt;
```&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=196</id>
		<title>Ε-Constraint-Methode</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=196"/>
		<updated>2026-05-19T13:33:24Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ε-Constraint-Methode =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;ε-Constraint-Methode&#039;&#039;&#039; (auch &#039;&#039;Epsilon-Constraint-Methode&#039;&#039;) ist ein Verfahren der [[Multi-Objective Optimization|Mehrzieloptimierung]], bei dem eine Zielfunktion optimiert wird, während die übrigen Zielfunktionen als Nebenbedingungen formuliert werden.&lt;br /&gt;
Die Methode gehört zu den klassischen Verfahren zur Berechnung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]] und wird insbesondere in der linearen, nichtlinearen sowie kombinatorischen Optimierung eingesetzt.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Bei vielen Optimierungsproblemen existieren mehrere Zielgrößen gleichzeitig.&lt;br /&gt;
Diese Ziele stehen häufig in Konkurrenz zueinander.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Minimierung der Kosten&lt;br /&gt;
* Minimierung von Emissionen&lt;br /&gt;
* Maximierung der Qualität&lt;br /&gt;
* Minimierung der Bearbeitungszeit&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wählt eines dieser Ziele als primäre Zielfunktion aus.&lt;br /&gt;
Alle anderen Ziele werden durch Grenzwerte (ε-Werte) eingeschränkt.&lt;br /&gt;
&lt;br /&gt;
Dadurch wird aus einem Mehrzielproblem ein klassisches Einzelzielproblem.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Formulierung ==&lt;br /&gt;
&lt;br /&gt;
Ein allgemeines Multi-Objective-Problem lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; (f_1(x), f_2(x), ..., f_k(x))&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in S&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;f_i(x)&amp;lt;/math&amp;gt; = Zielfunktionen&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; = Entscheidungsvariablen&lt;br /&gt;
* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; = zulässiger Lösungsraum&lt;br /&gt;
&lt;br /&gt;
=== Umformung mit der ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird ausgewählt, beispielsweise &amp;lt;math&amp;gt;f_1(x)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; f_1(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die übrigen Ziele werden in Nebenbedingungen umgewandelt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_3(x) \leq \varepsilon_3&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_k(x) \leq \varepsilon_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch entsteht ein gewöhnliches Optimierungsproblem.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung des ε-Wertes ==&lt;br /&gt;
&lt;br /&gt;
Der Parameter ε definiert einen maximal zulässigen Wert für eine Zielfunktion.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Kosten sollen minimiert werden&lt;br /&gt;
* CO₂-Ausstoß darf höchstens 100 kg betragen&lt;br /&gt;
&lt;br /&gt;
Dann lautet die Nebenbedingung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
CO_2(x) \leq 100&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Variation von ε können unterschiedliche Pareto-Lösungen erzeugt werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang mit Pareto-Optimalität ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode dient vor allem zur Erzeugung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]].&lt;br /&gt;
&lt;br /&gt;
Wird der ε-Wert systematisch verändert, entstehen verschiedene Kompromisslösungen zwischen den Zielgrößen.&lt;br /&gt;
&lt;br /&gt;
Dadurch lässt sich die sogenannte &#039;&#039;&#039;Pareto-Front&#039;&#039;&#039; approximieren.&lt;br /&gt;
&lt;br /&gt;
== Vorgehensweise ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Auswahl der Hauptzielfunktion ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird als primäres Optimierungsziel festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. Definition der ε-Grenzen ===&lt;br /&gt;
&lt;br /&gt;
Für die übrigen Ziele werden zulässige Grenzwerte festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Emissionen(x) \leq 50&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Naehrstoffabweichung(x) \leq 10&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. Lösung des Einzelzielproblems ===&lt;br /&gt;
&lt;br /&gt;
Das resultierende Problem wird mit klassischen Verfahren gelöst:&lt;br /&gt;
&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
&lt;br /&gt;
=== 4. Variation der ε-Werte ===&lt;br /&gt;
&lt;br /&gt;
Durch wiederholtes Lösen mit unterschiedlichen ε-Werten werden weitere Pareto-Lösungen erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Ein Unternehmen möchte:&lt;br /&gt;
&lt;br /&gt;
* Produktionskosten minimieren&lt;br /&gt;
* Energieverbrauch begrenzen&lt;br /&gt;
&lt;br /&gt;
=== Zielfunktionen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_1(x) = Kosten&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) = Energieverbrauch&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ε-Constraint-Formulierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Energieverbrauch(x) \leq \varepsilon&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun wird ε schrittweise verändert:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ε-Wert&lt;br /&gt;
&lt;br /&gt;
| ! Ergebnis                                           |&lt;br /&gt;
| ---------------------------------------------------- |&lt;br /&gt;
| 100                                                  |&lt;br /&gt;
| sehr günstige Lösung, hoher Energieverbrauch erlaubt |&lt;br /&gt;
| -                                                    |&lt;br /&gt;
| 80                                                   |&lt;br /&gt;
| ausgewogenere Lösung                                 |&lt;br /&gt;
| -                                                    |&lt;br /&gt;
| 50                                                   |&lt;br /&gt;
| energieeffiziente, aber teurere Lösung               |&lt;br /&gt;
| }                                                    |&lt;br /&gt;
&lt;br /&gt;
== Vorteile ==&lt;br /&gt;
&lt;br /&gt;
=== Gute Kontrolle über Nebenbedingungen ===&lt;br /&gt;
&lt;br /&gt;
Der Entscheider kann konkrete Grenzwerte vorgeben.&lt;br /&gt;
&lt;br /&gt;
=== Erzeugung echter Pareto-Lösungen ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann Pareto-optimale Lösungen systematisch erzeugen.&lt;br /&gt;
&lt;br /&gt;
=== Kompatibel mit klassischen Solvern ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann mit vorhandenen LP-, MILP- oder NLP-Solvern verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Besonders geeignet für diskrete Probleme ===&lt;br /&gt;
&lt;br /&gt;
Die Methode funktioniert oft besser als gewichtete Summenverfahren bei nicht-konvexen Pareto-Fronten.&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Wahl geeigneter ε-Werte schwierig ===&lt;br /&gt;
&lt;br /&gt;
Die Qualität der Ergebnisse hängt stark von den gewählten Grenzwerten ab.&lt;br /&gt;
&lt;br /&gt;
=== Hoher Rechenaufwand ===&lt;br /&gt;
&lt;br /&gt;
Für viele ε-Werte muss das Optimierungsproblem mehrfach gelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Möglicherweise unzulässige Probleme ===&lt;br /&gt;
&lt;br /&gt;
Zu strenge ε-Werte können dazu führen, dass keine zulässige Lösung existiert.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Verfahren ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Grundidee&lt;br /&gt;
! Vorteile&lt;br /&gt;
&lt;br /&gt;
| ! Nachteile                                    |&lt;br /&gt;
| ---------------------------------------------- |&lt;br /&gt;
| Gewichtete Summe                               |&lt;br /&gt;
| Kombination aller Ziele zu einer Funktion      |&lt;br /&gt;
| Einfach                                        |&lt;br /&gt;
| Probleme bei nicht-konvexen Fronten            |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| ε-Constraint-Methode                           |&lt;br /&gt;
| Ein Ziel optimieren, andere begrenzen          |&lt;br /&gt;
| Gute Pareto-Abdeckung                          |&lt;br /&gt;
| Mehrfache Optimierung notwendig                |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| Lexikographische Optimierung                   |&lt;br /&gt;
| Ziele nach Priorität ordnen                    |&lt;br /&gt;
| Klare Priorisierung                            |&lt;br /&gt;
| Niedrig priorisierte Ziele kaum berücksichtigt |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| Evolutionäre Verfahren                         |&lt;br /&gt;
| Population von Lösungen erzeugen               |&lt;br /&gt;
| Viele Pareto-Lösungen gleichzeitig             |&lt;br /&gt;
| Hoher Rechenaufwand                            |&lt;br /&gt;
| }                                              |&lt;br /&gt;
&lt;br /&gt;
== Erweiterte Varianten ==&lt;br /&gt;
&lt;br /&gt;
=== Adaptive ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Die ε-Werte werden dynamisch angepasst, um die Pareto-Front gleichmäßiger abzutasten.&lt;br /&gt;
&lt;br /&gt;
=== Augmented ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Zusätzliche Terme verhindern schwach Pareto-optimale Lösungen.&lt;br /&gt;
&lt;br /&gt;
=== Hybridverfahren ===&lt;br /&gt;
&lt;br /&gt;
Kombination mit:&lt;br /&gt;
&lt;br /&gt;
* [[Genetischer Algorithmus]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Simulated Annealing]]&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wird in vielen Bereichen eingesetzt:&lt;br /&gt;
&lt;br /&gt;
=== Produktion ===&lt;br /&gt;
&lt;br /&gt;
* Kosten vs. Qualität&lt;br /&gt;
* Energieverbrauch vs. Produktionsmenge&lt;br /&gt;
&lt;br /&gt;
=== Logistik ===&lt;br /&gt;
&lt;br /&gt;
* Transportkosten vs. Lieferzeit&lt;br /&gt;
* Emissionen vs. Effizienz&lt;br /&gt;
&lt;br /&gt;
=== Landwirtschaft ===&lt;br /&gt;
&lt;br /&gt;
* Kostenminimierung&lt;br /&gt;
* Minimierung von Stickstoffüberschüssen&lt;br /&gt;
* Minimierung von Nährstoffabweichungen&lt;br /&gt;
&lt;br /&gt;
=== Energietechnik ===&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit vs. CO₂-Ausstoß&lt;br /&gt;
* Netzstabilität vs. Kosten&lt;br /&gt;
&lt;br /&gt;
=== Softwareentwicklung ===&lt;br /&gt;
&lt;br /&gt;
* Laufzeit vs. Speicherverbrauch&lt;br /&gt;
* Performance vs. Energieeffizienz&lt;br /&gt;
&lt;br /&gt;
== Beispiel in der Düngemitteloptimierung ==&lt;br /&gt;
&lt;br /&gt;
In einem Düngemittel-Empfehlungssystem könnte folgende Zielfunktion minimiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Nährstoffabweichung(x) \leq \varepsilon_1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Stickstoffüberschuss(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Variation von &amp;lt;math&amp;gt;\varepsilon_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\varepsilon_2&amp;lt;/math&amp;gt; entstehen unterschiedliche Kompromisse zwischen:&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit&lt;br /&gt;
* Bedarfsdeckung&lt;br /&gt;
* Umweltwirkung&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang zur Pareto-Front ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode ist besonders wichtig, weil sie auch nicht-konvexe Bereiche der Pareto-Front finden kann.&lt;br /&gt;
&lt;br /&gt;
Gewichtete Summenverfahren scheitern häufig daran, solche Lösungen zu entdecken.&lt;br /&gt;
&lt;br /&gt;
Dadurch gilt die ε-Constraint-Methode als eines der wichtigsten klassischen Verfahren der Multi-Objective-Optimierung.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
&lt;br /&gt;
* Miettinen, K.: &#039;&#039;Nonlinear Multiobjective Optimization&#039;&#039;. Springer, 1999.&lt;br /&gt;
* Ehrgott, M.: &#039;&#039;Multicriteria Optimization&#039;&#039;. Springer, 2005.&lt;br /&gt;
* Deb, K.: &#039;&#039;Multi-Objective Optimization using Evolutionary Algorithms&#039;&#039;. Wiley, 2001.&lt;br /&gt;
* Steuer, R. E.: &#039;&#039;Multiple Criteria Optimization: Theory, Computation and Application&#039;&#039;. Wiley, 1986.&lt;br /&gt;
* Haimes, Y. Y.; Lasdon, L. S.; Wismer, D. A.: &#039;&#039;On a Bicriterion Formulation of the Problems of Integrated System Identification and System Optimization&#039;&#039;, IEEE Transactions on Systems, Man, and Cybernetics, 1971.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
* [[Lexikographische Optimierung]]&lt;br /&gt;
* [[Gewichtete Summe]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=195</id>
		<title>Ε-Constraint-Methode</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=%CE%95-Constraint-Methode&amp;diff=195"/>
		<updated>2026-05-19T13:27:44Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= ε-Constraint-Methode =  Die &amp;#039;&amp;#039;&amp;#039;ε-Constraint-Methode&amp;#039;&amp;#039;&amp;#039; (auch &amp;#039;&amp;#039;Epsilon-Constraint-Methode&amp;#039;&amp;#039;) ist ein Verfahren der Mehrzieloptimierung, bei dem eine Zielfunktion optimiert wird, während die übrigen Zielfunktionen als Nebenbedingungen formuliert werden. Die Methode gehört zu den klassischen Verfahren zur Berechnung von Pareto-optimalen Lösungen und wird insbesondere in der linearen, nichtlinea…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ε-Constraint-Methode =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;ε-Constraint-Methode&#039;&#039;&#039; (auch &#039;&#039;Epsilon-Constraint-Methode&#039;&#039;) ist ein Verfahren der [[Multi-Objective Optimization|Mehrzieloptimierung]], bei dem eine Zielfunktion optimiert wird, während die übrigen Zielfunktionen als Nebenbedingungen formuliert werden.&lt;br /&gt;
Die Methode gehört zu den klassischen Verfahren zur Berechnung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]] und wird insbesondere in der linearen, nichtlinearen sowie kombinatorischen Optimierung eingesetzt.&lt;br /&gt;
&lt;br /&gt;
== Grundidee ==&lt;br /&gt;
&lt;br /&gt;
Bei vielen Optimierungsproblemen existieren mehrere Zielgrößen gleichzeitig.&lt;br /&gt;
Diese Ziele stehen häufig in Konkurrenz zueinander.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Minimierung der Kosten&lt;br /&gt;
* Minimierung von Emissionen&lt;br /&gt;
* Maximierung der Qualität&lt;br /&gt;
* Minimierung der Bearbeitungszeit&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wählt eines dieser Ziele als primäre Zielfunktion aus.&lt;br /&gt;
Alle anderen Ziele werden durch Grenzwerte (ε-Werte) eingeschränkt.&lt;br /&gt;
&lt;br /&gt;
Dadurch wird aus einem Mehrzielproblem ein klassisches Einzelzielproblem.&lt;br /&gt;
&lt;br /&gt;
== Mathematische Formulierung ==&lt;br /&gt;
&lt;br /&gt;
Ein allgemeines Multi-Objective-Problem lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; (f_1(x), f_2(x), ..., f_k(x))&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in S&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;f_i(x)&amp;lt;/math&amp;gt; = Zielfunktionen&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; = Entscheidungsvariablen&lt;br /&gt;
* &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; = zulässiger Lösungsraum&lt;br /&gt;
&lt;br /&gt;
=== Umformung mit der ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird ausgewählt, beispielsweise &amp;lt;math&amp;gt;f_1(x)&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; f_1(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die übrigen Ziele werden in Nebenbedingungen umgewandelt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_3(x) \leq \varepsilon_3&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_k(x) \leq \varepsilon_k&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch entsteht ein gewöhnliches Optimierungsproblem.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung des ε-Wertes ==&lt;br /&gt;
&lt;br /&gt;
Der Parameter ε definiert einen maximal zulässigen Wert für eine Zielfunktion.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Kosten sollen minimiert werden&lt;br /&gt;
* CO₂-Ausstoß darf höchstens 100 kg betragen&lt;br /&gt;
&lt;br /&gt;
Dann lautet die Nebenbedingung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
CO_2(x) \leq 100&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Variation von ε können unterschiedliche Pareto-Lösungen erzeugt werden.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang mit Pareto-Optimalität ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode dient vor allem zur Erzeugung von [[Pareto-Optimierung|Pareto-optimalen Lösungen]].&lt;br /&gt;
&lt;br /&gt;
Wird der ε-Wert systematisch verändert, entstehen verschiedene Kompromisslösungen zwischen den Zielgrößen.&lt;br /&gt;
&lt;br /&gt;
Dadurch lässt sich die sogenannte &#039;&#039;&#039;Pareto-Front&#039;&#039;&#039; approximieren.&lt;br /&gt;
&lt;br /&gt;
== Vorgehensweise ==&lt;br /&gt;
&lt;br /&gt;
=== 1. Auswahl der Hauptzielfunktion ===&lt;br /&gt;
&lt;br /&gt;
Eine Zielfunktion wird als primäres Optimierungsziel festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. Definition der ε-Grenzen ===&lt;br /&gt;
&lt;br /&gt;
Für die übrigen Ziele werden zulässige Grenzwerte festgelegt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Emissionen(x) \leq 50&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Nährstoffabweichung(x) \leq 10&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. Lösung des Einzelzielproblems ===&lt;br /&gt;
&lt;br /&gt;
Das resultierende Problem wird mit klassischen Verfahren gelöst:&lt;br /&gt;
&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
&lt;br /&gt;
=== 4. Variation der ε-Werte ===&lt;br /&gt;
&lt;br /&gt;
Durch wiederholtes Lösen mit unterschiedlichen ε-Werten werden weitere Pareto-Lösungen erzeugt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel ==&lt;br /&gt;
&lt;br /&gt;
Ein Unternehmen möchte:&lt;br /&gt;
&lt;br /&gt;
* Produktionskosten minimieren&lt;br /&gt;
* Energieverbrauch begrenzen&lt;br /&gt;
&lt;br /&gt;
=== Zielfunktionen ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_1(x) = Kosten&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f_2(x) = Energieverbrauch&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== ε-Constraint-Formulierung ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Energieverbrauch(x) \leq \varepsilon&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun wird ε schrittweise verändert:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! ε-Wert&lt;br /&gt;
&lt;br /&gt;
| ! Ergebnis                                           |&lt;br /&gt;
| ---------------------------------------------------- |&lt;br /&gt;
| 100                                                  |&lt;br /&gt;
| sehr günstige Lösung, hoher Energieverbrauch erlaubt |&lt;br /&gt;
| -                                                    |&lt;br /&gt;
| 80                                                   |&lt;br /&gt;
| ausgewogenere Lösung                                 |&lt;br /&gt;
| -                                                    |&lt;br /&gt;
| 50                                                   |&lt;br /&gt;
| energieeffiziente, aber teurere Lösung               |&lt;br /&gt;
| }                                                    |&lt;br /&gt;
&lt;br /&gt;
== Vorteile ==&lt;br /&gt;
&lt;br /&gt;
=== Gute Kontrolle über Nebenbedingungen ===&lt;br /&gt;
&lt;br /&gt;
Der Entscheider kann konkrete Grenzwerte vorgeben.&lt;br /&gt;
&lt;br /&gt;
=== Erzeugung echter Pareto-Lösungen ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann Pareto-optimale Lösungen systematisch erzeugen.&lt;br /&gt;
&lt;br /&gt;
=== Kompatibel mit klassischen Solvern ===&lt;br /&gt;
&lt;br /&gt;
Die Methode kann mit vorhandenen LP-, MILP- oder NLP-Solvern verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Besonders geeignet für diskrete Probleme ===&lt;br /&gt;
&lt;br /&gt;
Die Methode funktioniert oft besser als gewichtete Summenverfahren bei nicht-konvexen Pareto-Fronten.&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Wahl geeigneter ε-Werte schwierig ===&lt;br /&gt;
&lt;br /&gt;
Die Qualität der Ergebnisse hängt stark von den gewählten Grenzwerten ab.&lt;br /&gt;
&lt;br /&gt;
=== Hoher Rechenaufwand ===&lt;br /&gt;
&lt;br /&gt;
Für viele ε-Werte muss das Optimierungsproblem mehrfach gelöst werden.&lt;br /&gt;
&lt;br /&gt;
=== Möglicherweise unzulässige Probleme ===&lt;br /&gt;
&lt;br /&gt;
Zu strenge ε-Werte können dazu führen, dass keine zulässige Lösung existiert.&lt;br /&gt;
&lt;br /&gt;
== Vergleich mit anderen Verfahren ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Verfahren&lt;br /&gt;
! Grundidee&lt;br /&gt;
! Vorteile&lt;br /&gt;
&lt;br /&gt;
| ! Nachteile                                    |&lt;br /&gt;
| ---------------------------------------------- |&lt;br /&gt;
| Gewichtete Summe                               |&lt;br /&gt;
| Kombination aller Ziele zu einer Funktion      |&lt;br /&gt;
| Einfach                                        |&lt;br /&gt;
| Probleme bei nicht-konvexen Fronten            |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| ε-Constraint-Methode                           |&lt;br /&gt;
| Ein Ziel optimieren, andere begrenzen          |&lt;br /&gt;
| Gute Pareto-Abdeckung                          |&lt;br /&gt;
| Mehrfache Optimierung notwendig                |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| Lexikographische Optimierung                   |&lt;br /&gt;
| Ziele nach Priorität ordnen                    |&lt;br /&gt;
| Klare Priorisierung                            |&lt;br /&gt;
| Niedrig priorisierte Ziele kaum berücksichtigt |&lt;br /&gt;
| -                                              |&lt;br /&gt;
| Evolutionäre Verfahren                         |&lt;br /&gt;
| Population von Lösungen erzeugen               |&lt;br /&gt;
| Viele Pareto-Lösungen gleichzeitig             |&lt;br /&gt;
| Hoher Rechenaufwand                            |&lt;br /&gt;
| }                                              |&lt;br /&gt;
&lt;br /&gt;
== Erweiterte Varianten ==&lt;br /&gt;
&lt;br /&gt;
=== Adaptive ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Die ε-Werte werden dynamisch angepasst, um die Pareto-Front gleichmäßiger abzutasten.&lt;br /&gt;
&lt;br /&gt;
=== Augmented ε-Constraint-Methode ===&lt;br /&gt;
&lt;br /&gt;
Zusätzliche Terme verhindern schwach Pareto-optimale Lösungen.&lt;br /&gt;
&lt;br /&gt;
=== Hybridverfahren ===&lt;br /&gt;
&lt;br /&gt;
Kombination mit:&lt;br /&gt;
&lt;br /&gt;
* [[Genetischer Algorithmus]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Simulated Annealing]]&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode wird in vielen Bereichen eingesetzt:&lt;br /&gt;
&lt;br /&gt;
=== Produktion ===&lt;br /&gt;
&lt;br /&gt;
* Kosten vs. Qualität&lt;br /&gt;
* Energieverbrauch vs. Produktionsmenge&lt;br /&gt;
&lt;br /&gt;
=== Logistik ===&lt;br /&gt;
&lt;br /&gt;
* Transportkosten vs. Lieferzeit&lt;br /&gt;
* Emissionen vs. Effizienz&lt;br /&gt;
&lt;br /&gt;
=== Landwirtschaft ===&lt;br /&gt;
&lt;br /&gt;
* Kostenminimierung&lt;br /&gt;
* Minimierung von Stickstoffüberschüssen&lt;br /&gt;
* Minimierung von Nährstoffabweichungen&lt;br /&gt;
&lt;br /&gt;
=== Energietechnik ===&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit vs. CO₂-Ausstoß&lt;br /&gt;
* Netzstabilität vs. Kosten&lt;br /&gt;
&lt;br /&gt;
=== Softwareentwicklung ===&lt;br /&gt;
&lt;br /&gt;
* Laufzeit vs. Speicherverbrauch&lt;br /&gt;
* Performance vs. Energieeffizienz&lt;br /&gt;
&lt;br /&gt;
== Beispiel in der Düngemitteloptimierung ==&lt;br /&gt;
&lt;br /&gt;
In einem Düngemittel-Empfehlungssystem könnte folgende Zielfunktion minimiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min \; Kosten(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
unter den Nebenbedingungen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Nährstoffabweichung(x) \leq \varepsilon_1&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
Stickstoffüberschuss(x) \leq \varepsilon_2&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Durch Variation von &amp;lt;math&amp;gt;\varepsilon_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;\varepsilon_2&amp;lt;/math&amp;gt; entstehen unterschiedliche Kompromisse zwischen:&lt;br /&gt;
&lt;br /&gt;
* Wirtschaftlichkeit&lt;br /&gt;
* Bedarfsdeckung&lt;br /&gt;
* Umweltwirkung&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang zur Pareto-Front ==&lt;br /&gt;
&lt;br /&gt;
Die ε-Constraint-Methode ist besonders wichtig, weil sie auch nicht-konvexe Bereiche der Pareto-Front finden kann.&lt;br /&gt;
&lt;br /&gt;
Gewichtete Summenverfahren scheitern häufig daran, solche Lösungen zu entdecken.&lt;br /&gt;
&lt;br /&gt;
Dadurch gilt die ε-Constraint-Methode als eines der wichtigsten klassischen Verfahren der Multi-Objective-Optimierung.&lt;br /&gt;
&lt;br /&gt;
== Literatur ==&lt;br /&gt;
&lt;br /&gt;
* Miettinen, K.: &#039;&#039;Nonlinear Multiobjective Optimization&#039;&#039;. Springer, 1999.&lt;br /&gt;
* Ehrgott, M.: &#039;&#039;Multicriteria Optimization&#039;&#039;. Springer, 2005.&lt;br /&gt;
* Deb, K.: &#039;&#039;Multi-Objective Optimization using Evolutionary Algorithms&#039;&#039;. Wiley, 2001.&lt;br /&gt;
* Steuer, R. E.: &#039;&#039;Multiple Criteria Optimization: Theory, Computation and Application&#039;&#039;. Wiley, 1986.&lt;br /&gt;
* Haimes, Y. Y.; Lasdon, L. S.; Wismer, D. A.: &#039;&#039;On a Bicriterion Formulation of the Problems of Integrated System Identification and System Optimization&#039;&#039;, IEEE Transactions on Systems, Man, and Cybernetics, 1971.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* [[NSGA-II]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Gemischt-ganzzahlige Optimierung]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Evolutionäre Algorithmen]]&lt;br /&gt;
* [[Lexikographische Optimierung]]&lt;br /&gt;
* [[Gewichtete Summe]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=194</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=194"/>
		<updated>2026-05-19T13:27:33Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Optimierungsverfahren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
** [[ε-Constraint-Methode]]&lt;br /&gt;
** [[]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Automatentheorie&amp;diff=193</id>
		<title>Automatentheorie</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Automatentheorie&amp;diff=193"/>
		<updated>2026-04-30T10:16:55Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „{{Infobox Fachgebiet | Name = Automatentheorie | Gebiet = Theoretische Informatik | Zentrale Objekte = Automaten, Zustände, Übergänge, formale Sprachen | Wichtige Modelle = Endliche Automaten, Kellerautomaten, Turingmaschinen, linear beschränkte Automaten | Verwandte Themen = Formale Sprachen, Berechenbarkeitstheorie, Komplexitätstheorie, Compilerbau }}  &amp;#039;&amp;#039;&amp;#039;Automatentheorie&amp;#039;&amp;#039;&amp;#039; ist ein Teilgebiet der [[Theoretische Informatik|theoretischen Informatik]…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Fachgebiet&lt;br /&gt;
| Name = Automatentheorie&lt;br /&gt;
| Gebiet = Theoretische Informatik&lt;br /&gt;
| Zentrale Objekte = Automaten, Zustände, Übergänge, formale Sprachen&lt;br /&gt;
| Wichtige Modelle = Endliche Automaten, Kellerautomaten, Turingmaschinen, linear beschränkte Automaten&lt;br /&gt;
| Verwandte Themen = Formale Sprachen, Berechenbarkeitstheorie, Komplexitätstheorie, Compilerbau&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Automatentheorie&#039;&#039;&#039; ist ein Teilgebiet der [[Theoretische Informatik|theoretischen Informatik]]. Sie beschäftigt sich mit abstrakten Rechenmodellen, sogenannten &#039;&#039;&#039;Automaten&#039;&#039;&#039;. Automaten dienen dazu, Berechnungen formal zu beschreiben und zu untersuchen, welche Arten von Problemen oder Sprachen durch bestimmte Maschinenmodelle erkannt oder entschieden werden können.&lt;br /&gt;
&lt;br /&gt;
Ein Automat kann vereinfacht als mathematisches Modell einer Maschine verstanden werden, die eine Eingabe liest, Zustände wechselt und am Ende entscheidet, ob die Eingabe akzeptiert oder verworfen wird. Die Automatentheorie bildet eine wichtige Grundlage für [[Formale Sprachen]], [[Compilerbau]], [[Berechenbarkeitstheorie]], [[Komplexitätstheorie]], [[Künstliche Intelligenz]], [[Softwareverifikation]] und viele weitere Bereiche der Informatik.&lt;br /&gt;
&lt;br /&gt;
== Grundidee der Automatentheorie ==&lt;br /&gt;
&lt;br /&gt;
Die Automatentheorie untersucht, wie sich Berechnungen mit einfachen mathematischen Modellen darstellen lassen. Dabei steht nicht die konkrete technische Umsetzung eines Computers im Vordergrund, sondern die abstrakte Struktur einer Berechnung.&lt;br /&gt;
&lt;br /&gt;
Ein Automat verarbeitet Eingaben schrittweise. Er befindet sich zu jedem Zeitpunkt in einem bestimmten Zustand. Abhängig vom aktuellen Zustand und dem gelesenen Eingabezeichen wechselt er in einen neuen Zustand.&lt;br /&gt;
&lt;br /&gt;
Typische Fragen der Automatentheorie sind:&lt;br /&gt;
&lt;br /&gt;
* Welche Eingaben akzeptiert ein Automat?&lt;br /&gt;
* Welche Sprache wird durch einen Automaten erkannt?&lt;br /&gt;
* Welcher Automatentyp ist für welche Sprachklasse geeignet?&lt;br /&gt;
* Kann ein Automat in ein anderes Modell umgewandelt werden?&lt;br /&gt;
* Welche Probleme sind mit einem bestimmten Automatenmodell entscheidbar?&lt;br /&gt;
* Welche Grenzen haben einfache Automatenmodelle?&lt;br /&gt;
&lt;br /&gt;
== Grundbegriffe ==&lt;br /&gt;
&lt;br /&gt;
=== Alphabet ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Alphabet&#039;&#039;&#039; ist eine endliche, nichtleere Menge von Zeichen. Es wird meistens mit &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{a,b\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{a,b,c,\dots,z\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Alphabet legt fest, aus welchen Zeichen Eingabewörter bestehen dürfen.&lt;br /&gt;
&lt;br /&gt;
=== Wort ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Wort&#039;&#039;&#039; ist eine endliche Folge von Zeichen aus einem Alphabet.&lt;br /&gt;
&lt;br /&gt;
Beispiele über dem Alphabet &amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;101&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;00110&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das leere Wort wird mit &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; bezeichnet. Es enthält kein Zeichen und hat die Länge 0.&lt;br /&gt;
&lt;br /&gt;
=== Sprache ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;formale Sprache&#039;&#039;&#039; ist eine Menge von Wörtern über einem Alphabet.&lt;br /&gt;
&lt;br /&gt;
Formal gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L \subseteq \Sigma^*&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei bezeichnet &amp;lt;math&amp;gt;\Sigma^*&amp;lt;/math&amp;gt; die Menge aller endlichen Wörter über dem Alphabet &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{w \in \{0,1\}^* \mid w \text{ endet auf } 01\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Sprache enthält alle binären Wörter, die auf die Zeichenfolge &amp;lt;math&amp;gt;01&amp;lt;/math&amp;gt; enden.&lt;br /&gt;
&lt;br /&gt;
=== Zustand ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Zustand&#039;&#039;&#039; beschreibt die aktuelle interne Situation eines Automaten. Ein Automat besitzt nur endlich viele oder, bei erweiterten Automatenmodellen, formal beschriebene Zustände.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Ein Automat, der prüfen soll, ob ein Wort auf &amp;lt;math&amp;gt;01&amp;lt;/math&amp;gt; endet, muss sich merken, ob zuletzt eine &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; gelesen wurde und ob danach eine &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; folgt.&lt;br /&gt;
&lt;br /&gt;
=== Startzustand ===&lt;br /&gt;
&lt;br /&gt;
Der &#039;&#039;&#039;Startzustand&#039;&#039;&#039; ist der Zustand, in dem sich ein Automat zu Beginn der Verarbeitung befindet. Er wird häufig mit &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Endzustand ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Endzustand&#039;&#039;&#039; oder &#039;&#039;&#039;akzeptierender Zustand&#039;&#039;&#039; ist ein Zustand, in dem der Automat eine Eingabe akzeptiert, wenn die Eingabe vollständig verarbeitet wurde.&lt;br /&gt;
&lt;br /&gt;
Die Menge der Endzustände wird häufig mit &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
=== Übergangsfunktion ===&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Übergangsfunktion&#039;&#039;&#039; beschreibt, wie ein Automat von einem Zustand in einen anderen Zustand wechselt.&lt;br /&gt;
&lt;br /&gt;
Bei einem deterministischen endlichen Automaten hat sie die Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times \Sigma \rightarrow Q&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Für jeden Zustand und jedes Eingabezeichen ist eindeutig festgelegt, welcher Folgezustand erreicht wird.&lt;br /&gt;
&lt;br /&gt;
== Automaten als Spracherkenner ==&lt;br /&gt;
&lt;br /&gt;
In der Automatentheorie betrachtet man Automaten häufig als &#039;&#039;&#039;Spracherkenner&#039;&#039;&#039;. Ein Automat bekommt ein Wort als Eingabe und entscheidet, ob dieses Wort zu einer bestimmten Sprache gehört.&lt;br /&gt;
&lt;br /&gt;
Ein Wort wird akzeptiert, wenn der Automat nach vollständigem Lesen der Eingabe in einem akzeptierenden Zustand endet.&lt;br /&gt;
&lt;br /&gt;
Ein Wort wird verworfen, wenn der Automat nach vollständigem Lesen der Eingabe nicht in einem akzeptierenden Zustand endet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Die Sprache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{w \in \{0,1\}^* \mid w \text{ enthält eine gerade Anzahl von Einsen}\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann durch einen endlichen Automaten erkannt werden. Der Automat benötigt nur zwei Zustände:&lt;br /&gt;
&lt;br /&gt;
* einen Zustand für „bisher gerade Anzahl von Einsen“,&lt;br /&gt;
* einen Zustand für „bisher ungerade Anzahl von Einsen“.&lt;br /&gt;
&lt;br /&gt;
== Wichtige Automatenmodelle ==&lt;br /&gt;
&lt;br /&gt;
Die wichtigsten Automatenmodelle sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Automatentyp&lt;br /&gt;
! Speicher&lt;br /&gt;
! Erkennt Sprachklasse&lt;br /&gt;
! Typische Anwendung&lt;br /&gt;
|-&lt;br /&gt;
| Endlicher Automat&lt;br /&gt;
| Kein zusätzlicher Speicher&lt;br /&gt;
| Reguläre Sprachen&lt;br /&gt;
| Mustererkennung, lexikalische Analyse&lt;br /&gt;
|-&lt;br /&gt;
| Kellerautomat&lt;br /&gt;
| Stapelspeicher&lt;br /&gt;
| Kontextfreie Sprachen&lt;br /&gt;
| Syntaxanalyse, Klammerstrukturen&lt;br /&gt;
|-&lt;br /&gt;
| Linear beschränkter Automat&lt;br /&gt;
| Linear beschränktes Band&lt;br /&gt;
| Kontext-sensitive Sprachen&lt;br /&gt;
| theoretische Sprachmodelle&lt;br /&gt;
|-&lt;br /&gt;
| Turingmaschine&lt;br /&gt;
| Unbeschränktes Band&lt;br /&gt;
| Rekursiv aufzählbare Sprachen&lt;br /&gt;
| allgemeines Berechnungsmodell&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Endliche Automaten ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;endlicher Automat&#039;&#039;&#039; ist das einfachste wichtige Automatenmodell. Er besitzt eine endliche Menge von Zuständen und liest ein Eingabewort Zeichen für Zeichen von links nach rechts.&lt;br /&gt;
&lt;br /&gt;
Endliche Automaten haben keinen zusätzlichen Speicher. Sie können sich daher nur endlich viele Informationen über die bisher gelesene Eingabe merken.&lt;br /&gt;
&lt;br /&gt;
Endliche Automaten erkennen genau die &#039;&#039;&#039;regulären Sprachen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Deterministischer endlicher Automat ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;deterministischer endlicher Automat&#039;&#039;&#039;, kurz &#039;&#039;&#039;DEA&#039;&#039;&#039;, ist ein Automat, bei dem für jeden Zustand und jedes Eingabezeichen genau ein Folgezustand festgelegt ist.&lt;br /&gt;
&lt;br /&gt;
Ein DEA wird formal als Tupel definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;A = (Q, \Sigma, \delta, q_0, F)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;&lt;br /&gt;
| endliche Menge von Zuständen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Eingabealphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;&lt;br /&gt;
| Übergangsfunktion&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startzustand&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der akzeptierenden Endzustände&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Übergangsfunktion lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times \Sigma \rightarrow Q&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Arbeitsweise eines DEA ===&lt;br /&gt;
&lt;br /&gt;
Ein DEA arbeitet folgendermaßen:&lt;br /&gt;
&lt;br /&gt;
# Der Automat startet im Startzustand &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;.&lt;br /&gt;
# Er liest das Eingabewort von links nach rechts.&lt;br /&gt;
# Für jedes Zeichen wird anhand der Übergangsfunktion der nächste Zustand bestimmt.&lt;br /&gt;
# Nach dem letzten Zeichen wird geprüft, ob der aktuelle Zustand ein Endzustand ist.&lt;br /&gt;
# Ist der Zustand ein Endzustand, wird das Wort akzeptiert.&lt;br /&gt;
# Andernfalls wird das Wort verworfen.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel eines DEA ===&lt;br /&gt;
&lt;br /&gt;
Gesucht ist ein Automat über dem Alphabet&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
der alle Wörter akzeptiert, die eine gerade Anzahl von Einsen enthalten.&lt;br /&gt;
&lt;br /&gt;
Die Zustände sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Zustand&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| bisher gerade Anzahl von Einsen gelesen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_1&amp;lt;/math&amp;gt;&lt;br /&gt;
| bisher ungerade Anzahl von Einsen gelesen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Der Startzustand ist &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;. Gleichzeitig ist &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt; auch der einzige Endzustand.&lt;br /&gt;
&lt;br /&gt;
Die Übergangstabelle lautet:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Aktueller Zustand&lt;br /&gt;
! Eingabe 0&lt;br /&gt;
! Eingabe 1&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;q_1&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_1&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;q_1&amp;lt;/math&amp;gt;&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine gelesene &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; ändert die Anzahl der Einsen nicht. Eine gelesene &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; wechselt zwischen gerade und ungerade.&lt;br /&gt;
&lt;br /&gt;
== Nichtdeterministischer endlicher Automat ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;nichtdeterministischer endlicher Automat&#039;&#039;&#039;, kurz &#039;&#039;&#039;NEA&#039;&#039;&#039;, erlaubt für einen Zustand und ein Eingabezeichen mehrere mögliche Folgezustände.&lt;br /&gt;
&lt;br /&gt;
Die Übergangsfunktion eines NEA hat die Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times \Sigma \rightarrow \mathcal{P}(Q)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei bezeichnet &amp;lt;math&amp;gt;\mathcal{P}(Q)&amp;lt;/math&amp;gt; die Potenzmenge von &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;, also die Menge aller Teilmengen von &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Ein NEA akzeptiert ein Wort, wenn es mindestens einen möglichen Rechenweg gibt, der in einem akzeptierenden Zustand endet.&lt;br /&gt;
&lt;br /&gt;
=== Unterschied zwischen DEA und NEA ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Eigenschaft&lt;br /&gt;
! DEA&lt;br /&gt;
! NEA&lt;br /&gt;
|-&lt;br /&gt;
| Folgezustand&lt;br /&gt;
| eindeutig bestimmt&lt;br /&gt;
| mehrere Möglichkeiten erlaubt&lt;br /&gt;
|-&lt;br /&gt;
| Berechnung&lt;br /&gt;
| genau ein Rechenweg&lt;br /&gt;
| mehrere mögliche Rechenwege&lt;br /&gt;
|-&lt;br /&gt;
| Akzeptanz&lt;br /&gt;
| Endzustand nach eindeutigem Lauf&lt;br /&gt;
| mindestens ein akzeptierender Lauf genügt&lt;br /&gt;
|-&lt;br /&gt;
| Ausdrucksstärke&lt;br /&gt;
| reguläre Sprachen&lt;br /&gt;
| reguläre Sprachen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Obwohl ein NEA flexibler wirkt, ist er nicht mächtiger als ein DEA. Jeder NEA kann in einen äquivalenten DEA umgewandelt werden. Dieses Verfahren heißt &#039;&#039;&#039;Potenzmengenkonstruktion&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Epsilon-NEA ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Epsilon-NEA&#039;&#039;&#039; ist ein nichtdeterministischer endlicher Automat, der zusätzlich sogenannte &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;-Übergänge erlaubt.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;-Übergang ist ein Zustandswechsel, bei dem kein Eingabezeichen gelesen wird.&lt;br /&gt;
&lt;br /&gt;
Die Übergangsfunktion lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times (\Sigma \cup \{\varepsilon\}) \rightarrow \mathcal{P}(Q)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch Epsilon-NEAs erkennen genau die regulären Sprachen.&lt;br /&gt;
&lt;br /&gt;
== Reguläre Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Eine Sprache heißt &#039;&#039;&#039;regulär&#039;&#039;&#039;, wenn sie durch einen endlichen Automaten erkannt werden kann.&lt;br /&gt;
&lt;br /&gt;
Reguläre Sprachen können äquivalent beschrieben werden durch:&lt;br /&gt;
&lt;br /&gt;
* deterministische endliche Automaten,&lt;br /&gt;
* nichtdeterministische endliche Automaten,&lt;br /&gt;
* Epsilon-NEAs,&lt;br /&gt;
* reguläre Ausdrücke,&lt;br /&gt;
* reguläre Grammatiken.&lt;br /&gt;
&lt;br /&gt;
=== Beispiele regulärer Sprachen ===&lt;br /&gt;
&lt;br /&gt;
Über dem Alphabet &amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt; sind folgende Sprachen regulär:&lt;br /&gt;
&lt;br /&gt;
* alle Wörter, die mit &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt; beginnen,&lt;br /&gt;
* alle Wörter, die auf &amp;lt;math&amp;gt;101&amp;lt;/math&amp;gt; enden,&lt;br /&gt;
* alle Wörter mit gerader Anzahl von Einsen,&lt;br /&gt;
* alle Wörter, die die Teilfolge &amp;lt;math&amp;gt;00&amp;lt;/math&amp;gt; enthalten,&lt;br /&gt;
* alle Wörter mit höchstens drei Zeichen.&lt;br /&gt;
&lt;br /&gt;
== Reguläre Ausdrücke ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;regulärer Ausdruck&#039;&#039;&#039; beschreibt eine reguläre Sprache durch eine formale Schreibweise.&lt;br /&gt;
&lt;br /&gt;
Wichtige Operationen sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Schreibweise&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
| das Zeichen &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;r \mid s&amp;lt;/math&amp;gt;&lt;br /&gt;
| Alternative: &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;rs&amp;lt;/math&amp;gt;&lt;br /&gt;
| Konkatenation von &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;r^*&amp;lt;/math&amp;gt;&lt;br /&gt;
| beliebig viele Wiederholungen von &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;&lt;br /&gt;
| leeres Wort&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
| leere Sprache&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(0 \mid 1)^*101&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser reguläre Ausdruck beschreibt alle binären Wörter, die auf &amp;lt;math&amp;gt;101&amp;lt;/math&amp;gt; enden.&lt;br /&gt;
&lt;br /&gt;
== Äquivalenz von Automaten und regulären Ausdrücken ==&lt;br /&gt;
&lt;br /&gt;
Ein wichtiger Satz der Automatentheorie lautet:&lt;br /&gt;
&lt;br /&gt;
: Eine Sprache ist genau dann regulär, wenn sie von einem endlichen Automaten erkannt werden kann.&lt;br /&gt;
&lt;br /&gt;
Gleichwertig gilt:&lt;br /&gt;
&lt;br /&gt;
: Eine Sprache ist genau dann regulär, wenn sie durch einen regulären Ausdruck beschrieben werden kann.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet, dass endliche Automaten und reguläre Ausdrücke dieselbe Ausdrucksstärke besitzen.&lt;br /&gt;
&lt;br /&gt;
== Potenzmengenkonstruktion ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Potenzmengenkonstruktion&#039;&#039;&#039; ist ein Verfahren zur Umwandlung eines NEA in einen äquivalenten DEA.&lt;br /&gt;
&lt;br /&gt;
Die Grundidee ist, dass ein Zustand des neuen DEA einer Menge von möglichen Zuständen des NEA entspricht.&lt;br /&gt;
&lt;br /&gt;
Wenn der NEA die Zustände&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;Q = \{q_0, q_1, q_2\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
hat, dann können die Zustände des konstruierten DEA Teilmengen davon sein, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\emptyset&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\{q_0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\{q_1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\{q_0, q_1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\{q_0, q_2\}&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;\{q_0, q_1, q_2\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der neue DEA simuliert also gleichzeitig alle möglichen Rechenwege des NEA.&lt;br /&gt;
&lt;br /&gt;
== Minimierung endlicher Automaten ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Automatenminimierung&#039;&#039;&#039; beschäftigt sich damit, zu einem gegebenen endlichen Automaten einen äquivalenten Automaten mit möglichst wenigen Zuständen zu finden.&lt;br /&gt;
&lt;br /&gt;
Zwei Zustände heißen äquivalent, wenn sie für alle möglichen Fortsetzungen der Eingabe dasselbe Akzeptanzverhalten besitzen.&lt;br /&gt;
&lt;br /&gt;
Ein minimaler DEA besitzt keine unterscheidbaren Zustände, die zusammengelegt werden können.&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung der Minimierung ===&lt;br /&gt;
&lt;br /&gt;
Die Minimierung ist wichtig, weil sie Automaten vereinfacht und effizienter macht.&lt;br /&gt;
&lt;br /&gt;
Anwendungen:&lt;br /&gt;
&lt;br /&gt;
* Optimierung von lexikalischen Scannern,&lt;br /&gt;
* Reduktion von Zustandsautomaten,&lt;br /&gt;
* formale Verifikation,&lt;br /&gt;
* Mustererkennung,&lt;br /&gt;
* Protokollanalyse.&lt;br /&gt;
&lt;br /&gt;
== Myhill-Nerode-Theorem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Myhill-Nerode-Theorem&#039;&#039;&#039; ist ein grundlegender Satz über reguläre Sprachen.&lt;br /&gt;
&lt;br /&gt;
Es charakterisiert reguläre Sprachen über Äquivalenzklassen von Wörtern. Vereinfacht besagt es:&lt;br /&gt;
&lt;br /&gt;
: Eine Sprache ist genau dann regulär, wenn sie nur endlich viele unterscheidbare Restsprachen besitzt.&lt;br /&gt;
&lt;br /&gt;
Anschaulich bedeutet das: Ein endlicher Automat kann nur endlich viele verschiedene Situationen unterscheiden. Wenn eine Sprache unendlich viele wirklich verschiedene Merksituationen benötigt, ist sie nicht regulär.&lt;br /&gt;
&lt;br /&gt;
Das Myhill-Nerode-Theorem wird verwendet, um:&lt;br /&gt;
&lt;br /&gt;
* die Regularität einer Sprache zu charakterisieren,&lt;br /&gt;
* minimale Automaten zu bestimmen,&lt;br /&gt;
* zu beweisen, dass bestimmte Sprachen nicht regulär sind.&lt;br /&gt;
&lt;br /&gt;
== Pumping-Lemma für reguläre Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Pumping-Lemma&#039;&#039;&#039; ist ein Werkzeug, um zu zeigen, dass eine Sprache nicht regulär ist.&lt;br /&gt;
&lt;br /&gt;
Es besagt vereinfacht:&lt;br /&gt;
&lt;br /&gt;
: Wenn eine Sprache regulär ist, dann gibt es eine Zahl &amp;lt;math&amp;gt;p&amp;lt;/math&amp;gt;, sodass jedes Wort &amp;lt;math&amp;gt;w&amp;lt;/math&amp;gt; der Sprache mit &amp;lt;math&amp;gt;|w| \geq p&amp;lt;/math&amp;gt; in drei Teile &amp;lt;math&amp;gt;w = xyz&amp;lt;/math&amp;gt; zerlegt werden kann, wobei der mittlere Teil &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; beliebig oft wiederholt werden darf und das Wort trotzdem in der Sprache bleibt.&lt;br /&gt;
&lt;br /&gt;
Formal gilt für reguläre Sprachen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;w = xyz&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;|xy| \leq p&amp;lt;/math&amp;gt;,&lt;br /&gt;
* &amp;lt;math&amp;gt;|y| &amp;gt; 0&amp;lt;/math&amp;gt;,&lt;br /&gt;
* &amp;lt;math&amp;gt;xy^iz \in L&amp;lt;/math&amp;gt; für alle &amp;lt;math&amp;gt;i \geq 0&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: Nicht-Regularität von a^n b^n ===&lt;br /&gt;
&lt;br /&gt;
Die Sprache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist nicht regulär.&lt;br /&gt;
&lt;br /&gt;
Ein endlicher Automat kann nicht beliebig viele &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;-Zeichen zählen, um anschließend genau dieselbe Anzahl von &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;-Zeichen zu überprüfen. Dafür wäre ein unbeschränkter Speicher erforderlich.&lt;br /&gt;
&lt;br /&gt;
== Kellerautomaten ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Kellerautomat&#039;&#039;&#039; ist ein Automat mit zusätzlichem Stapelspeicher. Dieser Stapel wird auch &#039;&#039;&#039;Keller&#039;&#039;&#039; genannt.&lt;br /&gt;
&lt;br /&gt;
Ein Kellerautomat kann:&lt;br /&gt;
&lt;br /&gt;
* Eingabezeichen lesen,&lt;br /&gt;
* Zustände wechseln,&lt;br /&gt;
* Symbole auf den Stapel legen,&lt;br /&gt;
* Symbole vom Stapel entfernen,&lt;br /&gt;
* das oberste Stapelsymbol betrachten.&lt;br /&gt;
&lt;br /&gt;
Kellerautomaten erkennen genau die &#039;&#039;&#039;kontextfreien Sprachen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Aufbau eines Kellerautomaten ==&lt;br /&gt;
&lt;br /&gt;
Ein Kellerautomat wird häufig als Tupel beschrieben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;A = (Q, \Sigma, \Gamma, \delta, q_0, Z_0, F)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;&lt;br /&gt;
| endliche Zustandsmenge&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Eingabealphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Gamma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Kelleralphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;&lt;br /&gt;
| Übergangsfunktion&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startzustand&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;Z_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startsymbol im Keller&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge akzeptierender Zustände&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Beispiel für eine kontextfreie Sprache ===&lt;br /&gt;
&lt;br /&gt;
Die Sprache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann durch einen Kellerautomaten erkannt werden.&lt;br /&gt;
&lt;br /&gt;
Die Idee ist:&lt;br /&gt;
&lt;br /&gt;
# Für jedes gelesene &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; legt der Automat ein Symbol auf den Keller.&lt;br /&gt;
# Für jedes gelesene &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; entfernt der Automat ein Symbol vom Keller.&lt;br /&gt;
# Am Ende wird akzeptiert, wenn der Keller wieder leer ist beziehungsweise nur noch das Startsymbol enthält.&lt;br /&gt;
&lt;br /&gt;
Dadurch kann der Kellerautomat die Anzahl der &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;-Zeichen mit der Anzahl der &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;-Zeichen vergleichen.&lt;br /&gt;
&lt;br /&gt;
== Deterministische und nichtdeterministische Kellerautomaten ==&lt;br /&gt;
&lt;br /&gt;
Bei endlichen Automaten sind deterministische und nichtdeterministische Varianten gleich mächtig. Bei Kellerautomaten ist das anders.&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;nichtdeterministischer Kellerautomat&#039;&#039;&#039; ist mächtiger als ein deterministischer Kellerautomat.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet:&lt;br /&gt;
&lt;br /&gt;
* Nichtdeterministische Kellerautomaten erkennen alle kontextfreien Sprachen.&lt;br /&gt;
* Deterministische Kellerautomaten erkennen nur eine echte Teilklasse der kontextfreien Sprachen.&lt;br /&gt;
&lt;br /&gt;
Diese Unterscheidung ist besonders im Compilerbau relevant. Viele Programmiersprachen sind so gestaltet, dass ihre Syntax deterministisch analysierbar ist.&lt;br /&gt;
&lt;br /&gt;
== Turingmaschinen ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Turingmaschine&#039;&#039;&#039; ist ein sehr mächtiges abstraktes Berechnungsmodell. Sie wurde von Alan Turing eingeführt und gilt als formales Modell allgemeiner Berechenbarkeit.&lt;br /&gt;
&lt;br /&gt;
Eine Turingmaschine besteht aus:&lt;br /&gt;
&lt;br /&gt;
* einer endlichen Zustandsmenge,&lt;br /&gt;
* einem Band mit Speicherzellen,&lt;br /&gt;
* einem Schreib-/Lesekopf,&lt;br /&gt;
* einem Eingabealphabet,&lt;br /&gt;
* einem Bandalphabet,&lt;br /&gt;
* einer Übergangsfunktion,&lt;br /&gt;
* einem Startzustand,&lt;br /&gt;
* akzeptierenden und verwerfenden Zuständen.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu endlichen Automaten und Kellerautomaten kann eine Turingmaschine auf ihrem Band lesen und schreiben. Der Schreib-/Lesekopf kann sich nach links und rechts bewegen.&lt;br /&gt;
&lt;br /&gt;
== Formaler Aufbau einer Turingmaschine ==&lt;br /&gt;
&lt;br /&gt;
Eine Turingmaschine kann als Tupel beschrieben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;M = (Q, \Sigma, \Gamma, \delta, q_0, q_{accept}, q_{reject})&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;&lt;br /&gt;
| endliche Menge von Zuständen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Eingabealphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Gamma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Bandalphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;&lt;br /&gt;
| Übergangsfunktion&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startzustand&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_{accept}&amp;lt;/math&amp;gt;&lt;br /&gt;
| akzeptierender Zustand&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_{reject}&amp;lt;/math&amp;gt;&lt;br /&gt;
| verwerfender Zustand&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Übergangsfunktion hat häufig die Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times \Gamma \rightarrow Q \times \Gamma \times \{L,R\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Abhängig vom aktuellen Zustand und dem gelesenen Bandsymbol schreibt die Turingmaschine ein neues Symbol, wechselt den Zustand und bewegt den Kopf nach links oder rechts.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung der Turingmaschine ==&lt;br /&gt;
&lt;br /&gt;
Die Turingmaschine ist eines der wichtigsten Modelle der Informatik, weil sie die theoretische Grenze algorithmischer Berechenbarkeit beschreibt.&lt;br /&gt;
&lt;br /&gt;
Nach der Church-Turing-These gilt:&lt;br /&gt;
&lt;br /&gt;
: Alles, was intuitiv algorithmisch berechenbar ist, kann auch von einer Turingmaschine berechnet werden.&lt;br /&gt;
&lt;br /&gt;
Die Turingmaschine ist damit nicht nur ein Automatenmodell, sondern auch ein Fundament der [[Berechenbarkeitstheorie]].&lt;br /&gt;
&lt;br /&gt;
== Entscheidbare und akzeptierbare Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Im Zusammenhang mit Turingmaschinen unterscheidet man häufig zwischen entscheidbaren und akzeptierbaren Sprachen.&lt;br /&gt;
&lt;br /&gt;
=== Entscheidbare Sprache ===&lt;br /&gt;
&lt;br /&gt;
Eine Sprache heißt &#039;&#039;&#039;entscheidbar&#039;&#039;&#039;, wenn es eine Turingmaschine gibt, die für jedes Eingabewort nach endlich vielen Schritten anhält und korrekt entscheidet, ob das Wort zur Sprache gehört.&lt;br /&gt;
&lt;br /&gt;
=== Akzeptierbare Sprache ===&lt;br /&gt;
&lt;br /&gt;
Eine Sprache heißt &#039;&#039;&#039;akzeptierbar&#039;&#039;&#039; oder &#039;&#039;&#039;rekursiv aufzählbar&#039;&#039;&#039;, wenn es eine Turingmaschine gibt, die jedes Wort der Sprache akzeptiert. Für Wörter, die nicht zur Sprache gehören, muss sie jedoch nicht unbedingt anhalten.&lt;br /&gt;
&lt;br /&gt;
Jede entscheidbare Sprache ist akzeptierbar, aber nicht jede akzeptierbare Sprache ist entscheidbar.&lt;br /&gt;
&lt;br /&gt;
== Das Halteproblem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Halteproblem&#039;&#039;&#039; ist ein klassisches Beispiel für ein unentscheidbares Problem.&lt;br /&gt;
&lt;br /&gt;
Es fragt:&lt;br /&gt;
&lt;br /&gt;
: Hält ein gegebenes Programm bei einer gegebenen Eingabe nach endlich vielen Schritten an?&lt;br /&gt;
&lt;br /&gt;
Alan Turing zeigte, dass es keinen allgemeinen Algorithmus geben kann, der diese Frage für alle Programme und Eingaben korrekt beantwortet.&lt;br /&gt;
&lt;br /&gt;
Das Halteproblem zeigt eine fundamentale Grenze automatischer Berechnung.&lt;br /&gt;
&lt;br /&gt;
== Linear beschränkte Automaten ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;linear beschränkter Automat&#039;&#039;&#039; ist eine eingeschränkte Turingmaschine. Sein Band ist nur linear in der Länge der Eingabe beschränkt.&lt;br /&gt;
&lt;br /&gt;
Linear beschränkte Automaten erkennen die &#039;&#039;&#039;kontext-sensitiven Sprachen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Sie stehen in der Chomsky-Hierarchie zwischen Kellerautomaten und allgemeinen Turingmaschinen.&lt;br /&gt;
&lt;br /&gt;
== Chomsky-Hierarchie und Automaten ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Chomsky-Hierarchie&#039;&#039;&#039; ordnet formale Sprachen nach ihrer Ausdrucksstärke. Jeder Sprachklasse entspricht ein bestimmtes Automatenmodell.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ&lt;br /&gt;
! Sprachklasse&lt;br /&gt;
! Grammatik&lt;br /&gt;
! Automatenmodell&lt;br /&gt;
|-&lt;br /&gt;
| Typ 3&lt;br /&gt;
| Reguläre Sprachen&lt;br /&gt;
| Reguläre Grammatiken&lt;br /&gt;
| Endliche Automaten&lt;br /&gt;
|-&lt;br /&gt;
| Typ 2&lt;br /&gt;
| Kontextfreie Sprachen&lt;br /&gt;
| Kontextfreie Grammatiken&lt;br /&gt;
| Kellerautomaten&lt;br /&gt;
|-&lt;br /&gt;
| Typ 1&lt;br /&gt;
| Kontext-sensitive Sprachen&lt;br /&gt;
| Kontext-sensitive Grammatiken&lt;br /&gt;
| Linear beschränkte Automaten&lt;br /&gt;
|-&lt;br /&gt;
| Typ 0&lt;br /&gt;
| Rekursiv aufzählbare Sprachen&lt;br /&gt;
| Unbeschränkte Grammatiken&lt;br /&gt;
| Turingmaschinen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Hierarchie zeigt, dass mächtigere Automatenmodelle größere Sprachklassen erkennen können.&lt;br /&gt;
&lt;br /&gt;
== Vergleich wichtiger Automatenmodelle ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Eigenschaft&lt;br /&gt;
! Endlicher Automat&lt;br /&gt;
! Kellerautomat&lt;br /&gt;
! Turingmaschine&lt;br /&gt;
|-&lt;br /&gt;
| Zustände&lt;br /&gt;
| endlich&lt;br /&gt;
| endlich&lt;br /&gt;
| endlich&lt;br /&gt;
|-&lt;br /&gt;
| Zusätzlicher Speicher&lt;br /&gt;
| keiner&lt;br /&gt;
| Stapel&lt;br /&gt;
| unbeschränktes Band&lt;br /&gt;
|-&lt;br /&gt;
| Speicherzugriff&lt;br /&gt;
| nicht vorhanden&lt;br /&gt;
| nur oberstes Stapelsymbol&lt;br /&gt;
| beliebige Bandbewegung schrittweise&lt;br /&gt;
|-&lt;br /&gt;
| Erkannte Sprachen&lt;br /&gt;
| regulär&lt;br /&gt;
| kontextfrei&lt;br /&gt;
| rekursiv aufzählbar&lt;br /&gt;
|-&lt;br /&gt;
| Beispielproblem&lt;br /&gt;
| Wort endet auf 01&lt;br /&gt;
| gleich viele a und b in Form a^n b^n&lt;br /&gt;
| allgemeine Berechnung&lt;br /&gt;
|-&lt;br /&gt;
| Ausdrucksstärke&lt;br /&gt;
| gering&lt;br /&gt;
| mittel&lt;br /&gt;
| sehr hoch&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Abschluss- und Entscheidungsprobleme ==&lt;br /&gt;
&lt;br /&gt;
In der Automatentheorie werden häufig Entscheidungsprobleme über Automaten und Sprachen untersucht.&lt;br /&gt;
&lt;br /&gt;
Typische Fragen sind:&lt;br /&gt;
&lt;br /&gt;
* Ist die Sprache eines Automaten leer?&lt;br /&gt;
* Sind zwei Automaten äquivalent?&lt;br /&gt;
* Akzeptiert ein Automat ein bestimmtes Wort?&lt;br /&gt;
* Ist eine Sprache regulär?&lt;br /&gt;
* Ist eine Grammatik mehrdeutig?&lt;br /&gt;
* Hält eine Turingmaschine auf einer bestimmten Eingabe?&lt;br /&gt;
&lt;br /&gt;
Einige dieser Probleme sind entscheidbar, andere nicht.&lt;br /&gt;
&lt;br /&gt;
== Leerheitsproblem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Leerheitsproblem&#039;&#039;&#039; fragt, ob die von einem Automaten erkannte Sprache leer ist.&lt;br /&gt;
&lt;br /&gt;
Für endliche Automaten lautet die Frage:&lt;br /&gt;
&lt;br /&gt;
: Gibt es mindestens ein Wort, das vom Automaten akzeptiert wird?&lt;br /&gt;
&lt;br /&gt;
Bei endlichen Automaten ist dieses Problem entscheidbar. Man prüft, ob ein Endzustand vom Startzustand aus erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
== Wortproblem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Wortproblem&#039;&#039;&#039; fragt, ob ein bestimmtes Wort von einem Automaten akzeptiert wird.&lt;br /&gt;
&lt;br /&gt;
Für endliche Automaten ist das Wortproblem leicht entscheidbar: Man simuliert den Automaten auf dem Eingabewort.&lt;br /&gt;
&lt;br /&gt;
Auch für Kellerautomaten und Turingmaschinen kann man entsprechende Varianten betrachten. Bei Turingmaschinen kann das Wortproblem jedoch problematisch werden, wenn die Maschine auf manchen Eingaben nicht anhält.&lt;br /&gt;
&lt;br /&gt;
== Äquivalenzproblem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Äquivalenzproblem&#039;&#039;&#039; fragt, ob zwei Automaten dieselbe Sprache erkennen.&lt;br /&gt;
&lt;br /&gt;
Für deterministische endliche Automaten ist das Äquivalenzproblem entscheidbar.&lt;br /&gt;
&lt;br /&gt;
Die Frage lautet:&lt;br /&gt;
&lt;br /&gt;
: Gilt &amp;lt;math&amp;gt;L(A_1) = L(A_2)&amp;lt;/math&amp;gt;?&lt;br /&gt;
&lt;br /&gt;
Man kann dies zum Beispiel prüfen, indem man einen Produktautomaten konstruiert und untersucht, ob es ein Wort gibt, das von genau einem der beiden Automaten akzeptiert wird.&lt;br /&gt;
&lt;br /&gt;
== Produktautomat ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Produktautomat&#039;&#039;&#039; kombiniert zwei Automaten zu einem neuen Automaten. Er wird häufig verwendet, um Abschlussoperationen oder Entscheidungsprobleme zu untersuchen.&lt;br /&gt;
&lt;br /&gt;
Wenn zwei Automaten die Zustandsmengen &amp;lt;math&amp;gt;Q_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;Q_2&amp;lt;/math&amp;gt; besitzen, dann hat der Produktautomat Zustände der Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(q_1, q_2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;q_1 \in Q_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;q_2 \in Q_2&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Der Produktautomat simuliert beide Automaten gleichzeitig.&lt;br /&gt;
&lt;br /&gt;
== Abschlusseigenschaften regulärer Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Reguläre Sprachen sind unter vielen Operationen abgeschlossen. Das bedeutet: Wenn man reguläre Sprachen mit bestimmten Operationen kombiniert, entsteht wieder eine reguläre Sprache.&lt;br /&gt;
&lt;br /&gt;
Reguläre Sprachen sind abgeschlossen unter:&lt;br /&gt;
&lt;br /&gt;
* Vereinigung,&lt;br /&gt;
* Schnitt,&lt;br /&gt;
* Komplement,&lt;br /&gt;
* Differenz,&lt;br /&gt;
* Konkatenation,&lt;br /&gt;
* Kleene-Stern,&lt;br /&gt;
* Spiegelung.&lt;br /&gt;
&lt;br /&gt;
Diese Eigenschaften lassen sich häufig mit Automatenkonstruktionen beweisen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Vereinigung regulärer Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Seien &amp;lt;math&amp;gt;L_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;L_2&amp;lt;/math&amp;gt; reguläre Sprachen.&lt;br /&gt;
&lt;br /&gt;
Dann ist auch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L_1 \cup L_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regulär.&lt;br /&gt;
&lt;br /&gt;
Begründung: Man kann einen Produktautomaten konstruieren, der beide Automaten parallel simuliert. Der neue Automat akzeptiert, wenn mindestens einer der beiden ursprünglichen Automaten akzeptiert.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Schnitt regulärer Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Seien &amp;lt;math&amp;gt;L_1&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;L_2&amp;lt;/math&amp;gt; reguläre Sprachen.&lt;br /&gt;
&lt;br /&gt;
Dann ist auch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L_1 \cap L_2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
regulär.&lt;br /&gt;
&lt;br /&gt;
Der Produktautomat akzeptiert in diesem Fall nur dann, wenn beide ursprünglichen Automaten akzeptieren.&lt;br /&gt;
&lt;br /&gt;
== Typische Beweismethoden ==&lt;br /&gt;
&lt;br /&gt;
In der Automatentheorie werden verschiedene mathematische Beweismethoden eingesetzt.&lt;br /&gt;
&lt;br /&gt;
Wichtige Methoden sind:&lt;br /&gt;
&lt;br /&gt;
* Konstruktion eines Automaten,&lt;br /&gt;
* Angabe einer Übergangstabelle,&lt;br /&gt;
* Induktionsbeweis über die Länge des Eingabewortes,&lt;br /&gt;
* Pumping-Lemma-Beweis,&lt;br /&gt;
* Myhill-Nerode-Argument,&lt;br /&gt;
* Reduktion,&lt;br /&gt;
* Simulation eines Automatenmodells durch ein anderes.&lt;br /&gt;
&lt;br /&gt;
== Typische Klausuraufgaben ==&lt;br /&gt;
&lt;br /&gt;
Typische Aufgaben zur Automatentheorie sind:&lt;br /&gt;
&lt;br /&gt;
* Konstruieren Sie einen DEA zu einer gegebenen Sprache.&lt;br /&gt;
* Geben Sie einen regulären Ausdruck zu einem Automaten an.&lt;br /&gt;
* Wandeln Sie einen NEA in einen DEA um.&lt;br /&gt;
* Minimieren Sie einen endlichen Automaten.&lt;br /&gt;
* Entscheiden Sie, ob eine Sprache regulär ist.&lt;br /&gt;
* Zeigen Sie mit dem Pumping-Lemma, dass eine Sprache nicht regulär ist.&lt;br /&gt;
* Konstruieren Sie einen Kellerautomaten für eine kontextfreie Sprache.&lt;br /&gt;
* Geben Sie eine Grammatik zu einem Automaten an.&lt;br /&gt;
* Ordnen Sie eine Sprache in die Chomsky-Hierarchie ein.&lt;br /&gt;
* Erklären Sie den Unterschied zwischen DEA, NEA und Epsilon-NEA.&lt;br /&gt;
* Beschreiben Sie die Arbeitsweise einer Turingmaschine.&lt;br /&gt;
* Zeigen Sie, dass ein Problem entscheidbar oder unentscheidbar ist.&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbereiche ==&lt;br /&gt;
&lt;br /&gt;
Obwohl Automaten abstrakte Modelle sind, haben sie viele praktische Anwendungen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bereich&lt;br /&gt;
! Anwendung der Automatentheorie&lt;br /&gt;
|-&lt;br /&gt;
| Compilerbau&lt;br /&gt;
| lexikalische Analyse und Syntaxanalyse&lt;br /&gt;
|-&lt;br /&gt;
| Reguläre Ausdrücke&lt;br /&gt;
| Suchmuster in Texten&lt;br /&gt;
|-&lt;br /&gt;
| Protokolle&lt;br /&gt;
| Modellierung von Zustandsübergängen&lt;br /&gt;
|-&lt;br /&gt;
| Softwareverifikation&lt;br /&gt;
| Prüfung von Programmzuständen&lt;br /&gt;
|-&lt;br /&gt;
| Hardwareentwurf&lt;br /&gt;
| Modellierung digitaler Schaltungen&lt;br /&gt;
|-&lt;br /&gt;
| Datenbanken&lt;br /&gt;
| Anfrageverarbeitung und formale Logik&lt;br /&gt;
|-&lt;br /&gt;
| Sprachverarbeitung&lt;br /&gt;
| formale Beschreibung syntaktischer Strukturen&lt;br /&gt;
|-&lt;br /&gt;
| Künstliche Intelligenz&lt;br /&gt;
| Zustandsräume und Suchverfahren&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Beispiel aus dem Compilerbau ==&lt;br /&gt;
&lt;br /&gt;
Im Compilerbau werden endliche Automaten häufig für die lexikalische Analyse verwendet.&lt;br /&gt;
&lt;br /&gt;
Ein Scanner zerlegt Quelltext in sogenannte Token. Beispiele für Token sind:&lt;br /&gt;
&lt;br /&gt;
* Schlüsselwörter,&lt;br /&gt;
* Bezeichner,&lt;br /&gt;
* Zahlen,&lt;br /&gt;
* Operatoren,&lt;br /&gt;
* Klammern.&lt;br /&gt;
&lt;br /&gt;
Reguläre Ausdrücke beschreiben dabei die Struktur der Token. Aus diesen regulären Ausdrücken können endliche Automaten erzeugt werden, die den Quelltext effizient erkennen.&lt;br /&gt;
&lt;br /&gt;
Ein Bezeichner in einer Programmiersprache könnte zum Beispiel durch folgenden regulären Ausdruck beschrieben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;[a-zA-Z][a-zA-Z0-9]^*&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Ausdruck beschreibt Wörter, die mit einem Buchstaben beginnen und danach aus Buchstaben oder Ziffern bestehen.&lt;br /&gt;
&lt;br /&gt;
== Beispiel aus der Protokollmodellierung ==&lt;br /&gt;
&lt;br /&gt;
Kommunikationsprotokolle lassen sich als Zustandsautomaten beschreiben.&lt;br /&gt;
&lt;br /&gt;
Ein stark vereinfachter Verbindungsaufbau könnte folgende Zustände besitzen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Zustand&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| CLOSED&lt;br /&gt;
| keine Verbindung&lt;br /&gt;
|-&lt;br /&gt;
| LISTEN&lt;br /&gt;
| wartet auf Verbindung&lt;br /&gt;
|-&lt;br /&gt;
| SYN_RECEIVED&lt;br /&gt;
| Verbindungsanfrage empfangen&lt;br /&gt;
|-&lt;br /&gt;
| ESTABLISHED&lt;br /&gt;
| Verbindung aufgebaut&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Übergänge entstehen durch Ereignisse wie eingehende Pakete, Zeitüberschreitungen oder Benutzeraktionen.&lt;br /&gt;
&lt;br /&gt;
== Grenzen einfacher Automaten ==&lt;br /&gt;
&lt;br /&gt;
Endliche Automaten sind sehr effizient, aber in ihrer Ausdrucksstärke beschränkt. Sie können keine beliebig großen Mengen zählen oder verschachtelte Strukturen allgemeiner Tiefe erkennen.&lt;br /&gt;
&lt;br /&gt;
Nicht regulär sind zum Beispiel Sprachen wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{ww \mid w \in \{0,1\}^*\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Solche Sprachen erfordern mehr Speicher als ein endlicher Automat besitzt.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang mit Berechenbarkeit und Komplexität ==&lt;br /&gt;
&lt;br /&gt;
Die Automatentheorie bildet eine Brücke zwischen formalen Sprachen, Berechenbarkeit und Komplexität.&lt;br /&gt;
&lt;br /&gt;
* Endliche Automaten zeigen, welche Sprachen mit sehr begrenztem Speicher erkannt werden können.&lt;br /&gt;
* Kellerautomaten erweitern dieses Modell um Stapelspeicher.&lt;br /&gt;
* Turingmaschinen beschreiben allgemeine Berechenbarkeit.&lt;br /&gt;
* Die Komplexitätstheorie untersucht anschließend, wie viele Ressourcen eine Berechnung benötigt.&lt;br /&gt;
&lt;br /&gt;
Damit ist die Automatentheorie eine Grundlage für das Verständnis der Frage, was Computer prinzipiell leisten können und wo ihre Grenzen liegen.&lt;br /&gt;
&lt;br /&gt;
== Wichtige Begriffe ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Begriff&lt;br /&gt;
! Erklärung&lt;br /&gt;
|-&lt;br /&gt;
| Automat&lt;br /&gt;
| abstraktes mathematisches Modell einer berechnenden Maschine&lt;br /&gt;
|-&lt;br /&gt;
| Zustand&lt;br /&gt;
| interne Situation eines Automaten&lt;br /&gt;
|-&lt;br /&gt;
| Startzustand&lt;br /&gt;
| Zustand, in dem die Verarbeitung beginnt&lt;br /&gt;
|-&lt;br /&gt;
| Endzustand&lt;br /&gt;
| akzeptierender Zustand eines Automaten&lt;br /&gt;
|-&lt;br /&gt;
| Übergangsfunktion&lt;br /&gt;
| Regel für Zustandswechsel&lt;br /&gt;
|-&lt;br /&gt;
| DEA&lt;br /&gt;
| deterministischer endlicher Automat&lt;br /&gt;
|-&lt;br /&gt;
| NEA&lt;br /&gt;
| nichtdeterministischer endlicher Automat&lt;br /&gt;
|-&lt;br /&gt;
| Epsilon-Übergang&lt;br /&gt;
| Zustandswechsel ohne Lesen eines Eingabezeichens&lt;br /&gt;
|-&lt;br /&gt;
| Reguläre Sprache&lt;br /&gt;
| Sprache, die von einem endlichen Automaten erkannt wird&lt;br /&gt;
|-&lt;br /&gt;
| Kellerautomat&lt;br /&gt;
| Automat mit Stapelspeicher&lt;br /&gt;
|-&lt;br /&gt;
| Turingmaschine&lt;br /&gt;
| allgemeines Modell algorithmischer Berechnung&lt;br /&gt;
|-&lt;br /&gt;
| Chomsky-Hierarchie&lt;br /&gt;
| Klassifikation formaler Sprachen&lt;br /&gt;
|-&lt;br /&gt;
| Pumping-Lemma&lt;br /&gt;
| Werkzeug zum Nachweis, dass eine Sprache nicht regulär ist&lt;br /&gt;
|-&lt;br /&gt;
| Potenzmengenkonstruktion&lt;br /&gt;
| Verfahren zur Umwandlung eines NEA in einen DEA&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
Die Automatentheorie untersucht abstrakte Maschinenmodelle und deren Fähigkeit, formale Sprachen zu erkennen. Sie beginnt bei einfachen endlichen Automaten, die reguläre Sprachen erkennen, und reicht über Kellerautomaten bis hin zu Turingmaschinen als allgemeinem Berechnungsmodell.&lt;br /&gt;
&lt;br /&gt;
Endliche Automaten sind besonders wichtig für reguläre Sprachen und praktische Anwendungen wie reguläre Ausdrücke, Scanner und Protokollmodellierung. Kellerautomaten erweitern dieses Modell um einen Stapelspeicher und können dadurch kontextfreie Sprachen erkennen. Turingmaschinen bilden schließlich ein umfassendes Modell für algorithmische Berechnung.&lt;br /&gt;
&lt;br /&gt;
Die Automatentheorie zeigt damit sowohl die Möglichkeiten als auch die Grenzen formaler Berechnungsmodelle. Sie ist ein zentrales Fundament der theoretischen Informatik und eng mit formalen Sprachen, Berechenbarkeitstheorie und Komplexitätstheorie verbunden.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Theoretische Informatik]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Reguläre Ausdrücke]]&lt;br /&gt;
* [[Deterministischer endlicher Automat]]&lt;br /&gt;
* [[Nichtdeterministischer endlicher Automat]]&lt;br /&gt;
* [[Kellerautomat]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Chomsky-Hierarchie]]&lt;br /&gt;
* [[Berechenbarkeitstheorie]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[Pumping-Lemma]]&lt;br /&gt;
* [[Myhill-Nerode-Theorem]]&lt;br /&gt;
&lt;br /&gt;
== Kategorien ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Theoretische Informatik]]&lt;br /&gt;
[[Kategorie:Automatentheorie]]&lt;br /&gt;
[[Kategorie:Formale Sprachen]]&lt;br /&gt;
[[Kategorie:Berechenbarkeit]]&lt;br /&gt;
[[Kategorie:Informatik]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=192</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=192"/>
		<updated>2026-04-30T10:09:50Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Theoretische Informatik */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
** [[P]]&lt;br /&gt;
** [[NP]]&lt;br /&gt;
** [[NP-schwer]]&lt;br /&gt;
** [[NP-vollständig]]&lt;br /&gt;
** [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Grundlagen_der_Theoretischen_Informatik&amp;diff=191</id>
		<title>Grundlagen der Theoretischen Informatik</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Grundlagen_der_Theoretischen_Informatik&amp;diff=191"/>
		<updated>2026-04-30T10:05:55Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „{{Infobox Fachgebiet | Name = Grundlagen der Theoretischen Informatik | Gebiet = Informatik | Teilgebiete = Automatentheorie, Formale Sprachen, Berechenbarkeitstheorie, Komplexitätstheorie, Logik | Zentrale Fragen = Was ist berechenbar? Wie effizient ist ein Problem lösbar? Wie lassen sich Sprachen, Automaten und Algorithmen formal beschreiben? }}  &amp;#039;&amp;#039;&amp;#039;Theoretische Informatik&amp;#039;&amp;#039;&amp;#039; ist ein Teilgebiet der Informatik, das sich mit den mathematischen Grundlage…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Fachgebiet&lt;br /&gt;
| Name = Grundlagen der Theoretischen Informatik&lt;br /&gt;
| Gebiet = Informatik&lt;br /&gt;
| Teilgebiete = Automatentheorie, Formale Sprachen, Berechenbarkeitstheorie, Komplexitätstheorie, Logik&lt;br /&gt;
| Zentrale Fragen = Was ist berechenbar? Wie effizient ist ein Problem lösbar? Wie lassen sich Sprachen, Automaten und Algorithmen formal beschreiben?&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Theoretische Informatik&#039;&#039;&#039; ist ein Teilgebiet der Informatik, das sich mit den mathematischen Grundlagen von Berechnung, Algorithmen, formalen Sprachen und Komplexität beschäftigt. Während die praktische Informatik häufig konkrete Systeme, Programme oder Anwendungen betrachtet, untersucht die theoretische Informatik abstrakte Modelle und allgemeine Gesetzmäßigkeiten. Sie beantwortet grundlegende Fragen wie: Was kann ein Computer prinzipiell berechnen? Welche Probleme sind algorithmisch lösbar? Welche Probleme sind zwar lösbar, aber nur mit sehr hohem Aufwand? Und wie kann man Programme, Sprachen und Berechnungen formal beschreiben?&lt;br /&gt;
&lt;br /&gt;
Die theoretische Informatik bildet damit eine wichtige Grundlage für viele andere Bereiche der Informatik, etwa Compilerbau, Kryptographie, Datenbanken, Softwareverifikation, künstliche Intelligenz und Algorithmik.&lt;br /&gt;
&lt;br /&gt;
== Überblick ==&lt;br /&gt;
&lt;br /&gt;
Die Grundlagen der theoretischen Informatik lassen sich grob in mehrere zentrale Themenbereiche einteilen:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Formale Sprachen und Grammatiken&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Automatentheorie&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Berechenbarkeitstheorie&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Komplexitätstheorie&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Logik und formale Systeme&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Algorithmische Grundlagen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese Bereiche hängen eng miteinander zusammen. Formale Sprachen beschreiben beispielsweise Mengen von Wörtern nach festen Regeln. Automaten können solche Sprachen erkennen. Berechenbarkeitstheorie untersucht, welche Probleme überhaupt mit einem Algorithmus lösbar sind. Komplexitätstheorie betrachtet, wie viel Zeit oder Speicherplatz zur Lösung eines Problems benötigt wird.&lt;br /&gt;
&lt;br /&gt;
== Grundbegriffe ==&lt;br /&gt;
&lt;br /&gt;
=== Alphabet ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Alphabet&#039;&#039;&#039; ist eine endliche, nichtleere Menge von Zeichen. In der theoretischen Informatik wird ein Alphabet häufig mit dem Symbol &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt; bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist ein binäres Alphabet mit den Zeichen 0 und 1.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{a,b,c\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies ist ein Alphabet mit drei Zeichen.&lt;br /&gt;
&lt;br /&gt;
=== Wort ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Wort&#039;&#039;&#039; ist eine endliche Folge von Zeichen aus einem Alphabet.&lt;br /&gt;
&lt;br /&gt;
Beispiele über dem Alphabet &amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;0&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;101&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;00110&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das leere Wort wird meist mit &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt; bezeichnet. Es enthält kein Zeichen und hat die Länge 0.&lt;br /&gt;
&lt;br /&gt;
=== Sprache ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;formale Sprache&#039;&#039;&#039; ist eine Menge von Wörtern über einem Alphabet.&lt;br /&gt;
&lt;br /&gt;
Formal gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L \subseteq \Sigma^*&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei bezeichnet &amp;lt;math&amp;gt;\Sigma^*&amp;lt;/math&amp;gt; die Menge aller endlichen Wörter über dem Alphabet &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{w \in \{0,1\}^* \mid w \text{ enthält eine gerade Anzahl von Einsen}\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Sprache enthält alle binären Wörter, in denen die Anzahl der Einsen gerade ist.&lt;br /&gt;
&lt;br /&gt;
== Formale Sprachen ==&lt;br /&gt;
&lt;br /&gt;
Formale Sprachen sind ein zentrales Konzept der theoretischen Informatik. Sie dienen dazu, syntaktische Strukturen exakt zu beschreiben. Ein wichtiges Anwendungsgebiet ist der Compilerbau, da Programmiersprachen durch formale Grammatiken beschrieben werden können.&lt;br /&gt;
&lt;br /&gt;
=== Grammatiken ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Grammatik&#039;&#039;&#039; ist ein Regelsystem zur Erzeugung von Wörtern einer Sprache. Sie besteht typischerweise aus:&lt;br /&gt;
&lt;br /&gt;
* einer Menge von Nichtterminalsymbolen,&lt;br /&gt;
* einer Menge von Terminalsymbolen,&lt;br /&gt;
* einem Startsymbol,&lt;br /&gt;
* einer Menge von Produktionsregeln.&lt;br /&gt;
&lt;br /&gt;
Eine Grammatik wird häufig als Tupel dargestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;G = (N, T, P, S)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;N&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der Nichtterminalsymbole&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;T&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der Terminalsymbole&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der Produktionsregeln&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startsymbol&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Beispiel einer Grammatik ===&lt;br /&gt;
&lt;br /&gt;
Gegeben sei die Grammatik:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;G = (\{S\}, \{a,b\}, P, S)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit den Produktionsregeln:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
S -&amp;gt; aSb&lt;br /&gt;
S -&amp;gt; ε&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Grammatik erzeugt die Sprache:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Sprache enthält also Wörter wie:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ε&lt;br /&gt;
ab&lt;br /&gt;
aabb&lt;br /&gt;
aaabbb&lt;br /&gt;
aaaabbbb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Chomsky-Hierarchie ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Chomsky-Hierarchie&#039;&#039;&#039; ordnet formale Grammatiken und Sprachen nach ihrer Ausdrucksstärke. Sie ist eine der wichtigsten Klassifikationen in der theoretischen Informatik.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ&lt;br /&gt;
! Bezeichnung&lt;br /&gt;
! Erzeugte Sprachen&lt;br /&gt;
! Erkennendes Maschinenmodell&lt;br /&gt;
|-&lt;br /&gt;
| Typ 0&lt;br /&gt;
| Unbeschränkte Grammatiken&lt;br /&gt;
| Rekursiv aufzählbare Sprachen&lt;br /&gt;
| Turingmaschine&lt;br /&gt;
|-&lt;br /&gt;
| Typ 1&lt;br /&gt;
| Kontext-sensitive Grammatiken&lt;br /&gt;
| Kontext-sensitive Sprachen&lt;br /&gt;
| Linear beschränkter Automat&lt;br /&gt;
|-&lt;br /&gt;
| Typ 2&lt;br /&gt;
| Kontextfreie Grammatiken&lt;br /&gt;
| Kontextfreie Sprachen&lt;br /&gt;
| Kellerautomat&lt;br /&gt;
|-&lt;br /&gt;
| Typ 3&lt;br /&gt;
| Reguläre Grammatiken&lt;br /&gt;
| Reguläre Sprachen&lt;br /&gt;
| Endlicher Automat&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Hierarchie ist echt gestuft. Jede reguläre Sprache ist auch kontextfrei, jede kontextfreie Sprache ist auch kontextsensitiv, und jede kontextsensitive Sprache ist auch rekursiv aufzählbar. Umgekehrt gilt dies jedoch nicht immer.&lt;br /&gt;
&lt;br /&gt;
== Reguläre Sprachen ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Reguläre Sprachen&#039;&#039;&#039; sind die einfachste Klasse formaler Sprachen innerhalb der Chomsky-Hierarchie. Sie können durch endliche Automaten, reguläre Ausdrücke oder reguläre Grammatiken beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
=== Beispiele regulärer Sprachen ===&lt;br /&gt;
&lt;br /&gt;
Über dem Alphabet &amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt; sind folgende Sprachen regulär:&lt;br /&gt;
&lt;br /&gt;
* Alle Wörter, die mit 0 beginnen.&lt;br /&gt;
* Alle Wörter, die eine gerade Anzahl von Einsen enthalten.&lt;br /&gt;
* Alle Wörter, die auf 101 enden.&lt;br /&gt;
* Alle Wörter mit höchstens drei Zeichen.&lt;br /&gt;
&lt;br /&gt;
=== Reguläre Ausdrücke ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;regulärer Ausdruck&#039;&#039;&#039; beschreibt eine reguläre Sprache. Wichtige Operatoren sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Operator&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
| Das Zeichen &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;r \mid s&amp;lt;/math&amp;gt;&lt;br /&gt;
| Alternative: &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;rs&amp;lt;/math&amp;gt;&lt;br /&gt;
| Konkatenation von &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;r^*&amp;lt;/math&amp;gt;&lt;br /&gt;
| Beliebig viele Wiederholungen von &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;&lt;br /&gt;
| Leeres Wort&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(0 \mid 1)^*101&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser reguläre Ausdruck beschreibt alle binären Wörter, die auf 101 enden.&lt;br /&gt;
&lt;br /&gt;
== Automatentheorie ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Automatentheorie&#039;&#039;&#039; untersucht abstrakte Maschinenmodelle. Diese Modelle dienen dazu, formale Sprachen zu erkennen oder Berechnungen zu beschreiben.&lt;br /&gt;
&lt;br /&gt;
Wichtige Automatenmodelle sind:&lt;br /&gt;
&lt;br /&gt;
* endliche Automaten,&lt;br /&gt;
* Kellerautomaten,&lt;br /&gt;
* linear beschränkte Automaten,&lt;br /&gt;
* Turingmaschinen.&lt;br /&gt;
&lt;br /&gt;
== Endliche Automaten ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;endlicher Automat&#039;&#039;&#039; ist ein einfaches Berechnungsmodell mit endlich vielen Zuständen. Er liest ein Eingabewort Zeichen für Zeichen und wechselt abhängig vom aktuellen Zustand und Eingabezeichen in einen neuen Zustand.&lt;br /&gt;
&lt;br /&gt;
Endliche Automaten erkennen genau die regulären Sprachen.&lt;br /&gt;
&lt;br /&gt;
=== Deterministischer endlicher Automat ===&lt;br /&gt;
&lt;br /&gt;
Ein deterministischer endlicher Automat, kurz &#039;&#039;&#039;DEA&#039;&#039;&#039;, wird formal beschrieben als:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;A = (Q, \Sigma, \delta, q_0, F)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei gilt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;Q&amp;lt;/math&amp;gt;&lt;br /&gt;
| Endliche Menge von Zuständen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\Sigma&amp;lt;/math&amp;gt;&lt;br /&gt;
| Eingabealphabet&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\delta&amp;lt;/math&amp;gt;&lt;br /&gt;
| Übergangsfunktion&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;q_0&amp;lt;/math&amp;gt;&lt;br /&gt;
| Startzustand&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;F&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge akzeptierender Endzustände&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Übergangsfunktion hat die Form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\delta : Q \times \Sigma \rightarrow Q&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Für jeden Zustand und jedes Eingabezeichen ist eindeutig festgelegt, in welchen Folgezustand der Automat wechselt.&lt;br /&gt;
&lt;br /&gt;
=== Nichtdeterministischer endlicher Automat ===&lt;br /&gt;
&lt;br /&gt;
Ein nichtdeterministischer endlicher Automat, kurz &#039;&#039;&#039;NEA&#039;&#039;&#039;, darf für einen Zustand und ein Eingabezeichen mehrere mögliche Folgezustände besitzen. Außerdem können sogenannte &amp;lt;math&amp;gt;\varepsilon&amp;lt;/math&amp;gt;-Übergänge erlaubt sein, bei denen der Automat den Zustand wechseln kann, ohne ein Zeichen zu lesen.&lt;br /&gt;
&lt;br /&gt;
Obwohl NEAs auf den ersten Blick mächtiger wirken, erkennen sie genau dieselbe Sprachklasse wie DEAs, nämlich die regulären Sprachen. Jeder NEA kann in einen äquivalenten DEA umgewandelt werden.&lt;br /&gt;
&lt;br /&gt;
== Kellerautomaten ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Kellerautomat&#039;&#039;&#039; ist ein Automat, der zusätzlich zu seinen Zuständen einen Speicher in Form eines Stapels besitzt. Dieser Stapel wird auch &#039;&#039;&#039;Keller&#039;&#039;&#039; genannt. Ein Kellerautomat kann Symbole auf den Stapel legen und wieder entfernen.&lt;br /&gt;
&lt;br /&gt;
Kellerautomaten erkennen genau die kontextfreien Sprachen.&lt;br /&gt;
&lt;br /&gt;
Ein typisches Beispiel für eine kontextfreie Sprache ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Sprache ist nicht regulär, weil ein endlicher Automat sich nicht beliebig viele gelesene &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;-Zeichen merken kann. Ein Kellerautomat kann dagegen für jedes gelesene &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt; ein Symbol auf den Stapel legen und für jedes gelesene &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; wieder ein Symbol entfernen.&lt;br /&gt;
&lt;br /&gt;
== Turingmaschinen ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Turingmaschine&#039;&#039;&#039; ist eines der wichtigsten Modelle der theoretischen Informatik. Sie wurde von Alan Turing eingeführt und dient als mathematisches Modell für allgemeine Berechnung.&lt;br /&gt;
&lt;br /&gt;
Eine Turingmaschine besitzt:&lt;br /&gt;
&lt;br /&gt;
* eine endliche Zustandsmenge,&lt;br /&gt;
* ein Eingabeband,&lt;br /&gt;
* einen Schreib-/Lesekopf,&lt;br /&gt;
* eine Übergangsfunktion,&lt;br /&gt;
* Start-, Akzeptier- und Verwerfzustände.&lt;br /&gt;
&lt;br /&gt;
Im Gegensatz zu einem endlichen Automaten kann eine Turingmaschine nicht nur lesen, sondern auch schreiben. Außerdem kann sich der Schreib-/Lesekopf nach links und rechts bewegen.&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung der Turingmaschine ===&lt;br /&gt;
&lt;br /&gt;
Die Turingmaschine ist deshalb so wichtig, weil sie als formales Modell für das gilt, was algorithmisch berechenbar ist. Viele moderne Computer sind praktisch viel komplexer als Turingmaschinen, aber im theoretischen Sinn nicht mächtiger: Alles, was ein realer Computer berechnen kann, kann auch eine Turingmaschine berechnen.&lt;br /&gt;
&lt;br /&gt;
== Berechenbarkeitstheorie ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Berechenbarkeitstheorie&#039;&#039;&#039; untersucht, welche Probleme durch Algorithmen lösbar sind. Dabei geht es nicht darum, ob ein Problem effizient lösbar ist, sondern ob überhaupt ein Algorithmus existiert, der für jede zulässige Eingabe eine korrekte Antwort liefert.&lt;br /&gt;
&lt;br /&gt;
=== Entscheidungsprobleme ===&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Entscheidungsproblem&#039;&#039;&#039; ist ein Problem, dessen Antwort entweder „ja“ oder „nein“ lautet.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
* Ist eine gegebene Zahl eine Primzahl?&lt;br /&gt;
* Enthält ein Graph einen Kreis?&lt;br /&gt;
* Ist ein gegebenes Wort Element einer bestimmten Sprache?&lt;br /&gt;
* Hält ein bestimmtes Programm bei einer bestimmten Eingabe?&lt;br /&gt;
&lt;br /&gt;
Viele Probleme der theoretischen Informatik werden als Entscheidungsprobleme formuliert, weil sie sich gut formal untersuchen lassen.&lt;br /&gt;
&lt;br /&gt;
=== Entscheidbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Ein Problem heißt &#039;&#039;&#039;entscheidbar&#039;&#039;&#039;, wenn es eine Turingmaschine gibt, die für jede Eingabe nach endlich vielen Schritten anhält und die korrekte Antwort liefert.&lt;br /&gt;
&lt;br /&gt;
Ein Problem heißt &#039;&#039;&#039;unentscheidbar&#039;&#039;&#039;, wenn kein solcher Algorithmus existiert.&lt;br /&gt;
&lt;br /&gt;
=== Semi-Entscheidbarkeit ===&lt;br /&gt;
&lt;br /&gt;
Ein Problem heißt &#039;&#039;&#039;semi-entscheidbar&#039;&#039;&#039;, wenn es eine Turingmaschine gibt, die bei Ja-Instanzen anhält und akzeptiert, bei Nein-Instanzen aber möglicherweise unendlich lange weiterläuft.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Positive Fälle können erkannt werden, negative Fälle aber nicht immer zuverlässig.&lt;br /&gt;
&lt;br /&gt;
== Das Halteproblem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Halteproblem&#039;&#039;&#039; ist ein klassisches Beispiel für ein unentscheidbares Problem.&lt;br /&gt;
&lt;br /&gt;
Es lautet:&lt;br /&gt;
&lt;br /&gt;
: Gegeben sei ein Programm &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; und eine Eingabe &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;. Hält &amp;lt;math&amp;gt;P&amp;lt;/math&amp;gt; bei Eingabe &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; nach endlich vielen Schritten an?&lt;br /&gt;
&lt;br /&gt;
Alan Turing zeigte, dass es keinen allgemeinen Algorithmus geben kann, der dieses Problem für alle Programme und Eingaben korrekt entscheidet.&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung des Halteproblems ===&lt;br /&gt;
&lt;br /&gt;
Das Halteproblem zeigt eine fundamentale Grenze der Informatik. Es beweist, dass es Probleme gibt, die grundsätzlich nicht algorithmisch lösbar sind, unabhängig davon, wie schnell oder leistungsfähig ein Computer ist.&lt;br /&gt;
&lt;br /&gt;
Dies ist ein wichtiger Unterschied zur Komplexitätstheorie: Dort geht es um den Aufwand lösbarer Probleme. Beim Halteproblem geht es darum, dass überhaupt keine allgemeine algorithmische Lösung existiert.&lt;br /&gt;
&lt;br /&gt;
== Komplexitätstheorie ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Komplexitätstheorie&#039;&#039;&#039; untersucht den Ressourcenverbrauch von Algorithmen. Besonders wichtig sind dabei:&lt;br /&gt;
&lt;br /&gt;
* Zeitaufwand,&lt;br /&gt;
* Speicheraufwand,&lt;br /&gt;
* Eingabegröße.&lt;br /&gt;
&lt;br /&gt;
Ein Problem kann also berechenbar sein, aber trotzdem praktisch kaum lösbar, wenn der Aufwand zu groß ist.&lt;br /&gt;
&lt;br /&gt;
=== Zeitkomplexität ===&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Zeitkomplexität&#039;&#039;&#039; beschreibt, wie viele Rechenschritte ein Algorithmus in Abhängigkeit von der Eingabegröße benötigt.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Notation&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Einordnung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Konstante Laufzeit&lt;br /&gt;
| Sehr effizient&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Logarithmische Laufzeit&lt;br /&gt;
| Sehr effizient&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Lineare Laufzeit&lt;br /&gt;
| Effizient&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(n \log n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Linear-logarithmische Laufzeit&lt;br /&gt;
| Häufig bei effizienten Sortierverfahren&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Quadratische Laufzeit&lt;br /&gt;
| Für große Eingaben oft problematisch&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;O(2^n)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Exponentielle Laufzeit&lt;br /&gt;
| Meist praktisch schwer handhabbar&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Speicherkomplexität ===&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Speicherkomplexität&#039;&#039;&#039; beschreibt, wie viel Speicherplatz ein Algorithmus abhängig von der Eingabegröße benötigt.&lt;br /&gt;
&lt;br /&gt;
Auch hier verwendet man häufig die O-Notation, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt; für konstanten Speicher,&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; für linearen Speicher,&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; für quadratischen Speicher.&lt;br /&gt;
&lt;br /&gt;
== O-Notation ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;O-Notation&#039;&#039;&#039; beschreibt das asymptotische Wachstum einer Funktion. Sie wird verwendet, um den Ressourcenverbrauch eines Algorithmus für große Eingaben abzuschätzen.&lt;br /&gt;
&lt;br /&gt;
Wenn ein Algorithmus beispielsweise eine Laufzeit von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
hat, bedeutet das, dass seine Laufzeit höchstens proportional zu &amp;lt;math&amp;gt;n^2&amp;lt;/math&amp;gt; wächst, wenn die Eingabegröße &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; groß wird.&lt;br /&gt;
&lt;br /&gt;
Die O-Notation ignoriert konstante Faktoren und niedrigere Terme. Daher gilt beispielsweise:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;3n^2 + 5n + 7 \in O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Algorithmusanalyse interessiert man sich also vor allem für den dominierenden Term.&lt;br /&gt;
&lt;br /&gt;
== Komplexitätsklassen ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Komplexitätsklasse&#039;&#039;&#039; ist eine Menge von Problemen, die unter bestimmten Ressourcenbeschränkungen lösbar sind.&lt;br /&gt;
&lt;br /&gt;
Wichtige Komplexitätsklassen sind:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;NP&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;PSPACE&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;EXPTIME&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Die Klasse P ==&lt;br /&gt;
&lt;br /&gt;
Die Klasse &#039;&#039;&#039;P&#039;&#039;&#039; enthält alle Entscheidungsprobleme, die von einer deterministischen Turingmaschine in polynomialer Zeit entschieden werden können.&lt;br /&gt;
&lt;br /&gt;
Polynomial bedeutet, dass die Laufzeit durch ein Polynom beschränkt ist, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt;&lt;br /&gt;
* &amp;lt;math&amp;gt;O(n^3)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Probleme in P gelten im Allgemeinen als effizient lösbar.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Probleme in P:&lt;br /&gt;
&lt;br /&gt;
* Sortieren einer Liste,&lt;br /&gt;
* Kürzeste Wege in Graphen mit nichtnegativen Kantengewichten,&lt;br /&gt;
* Testen, ob eine Zahl durch eine andere teilbar ist,&lt;br /&gt;
* Erreichbarkeit in bestimmten Graphmodellen.&lt;br /&gt;
&lt;br /&gt;
== Die Klasse NP ==&lt;br /&gt;
&lt;br /&gt;
Die Klasse &#039;&#039;&#039;NP&#039;&#039;&#039; enthält alle Entscheidungsprobleme, deren Ja-Lösungen in polynomialer Zeit überprüft werden können.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist: NP bedeutet nicht „nicht polynomial“. NP steht für &#039;&#039;&#039;nondeterministic polynomial time&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein Problem liegt in NP, wenn man eine vorgeschlagene Lösung effizient überprüfen kann.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
Beim Problem des Handlungsreisenden in Entscheidungsform fragt man:&lt;br /&gt;
&lt;br /&gt;
: Gibt es eine Rundreise mit Gesamtlänge höchstens &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt;?&lt;br /&gt;
&lt;br /&gt;
Wenn jemand eine konkrete Rundreise vorgibt, kann man in polynomialer Zeit prüfen, ob sie alle Städte genau einmal besucht und ob ihre Länge höchstens &amp;lt;math&amp;gt;k&amp;lt;/math&amp;gt; beträgt.&lt;br /&gt;
&lt;br /&gt;
== NP-schwere Probleme ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem heißt &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;, wenn jedes Problem aus NP in polynomialer Zeit auf dieses Problem reduziert werden kann.&lt;br /&gt;
&lt;br /&gt;
Anschaulich bedeutet das: Ein NP-schweres Problem ist mindestens so schwer wie alle Probleme aus NP.&lt;br /&gt;
&lt;br /&gt;
Ein NP-schweres Problem muss selbst nicht zwingend in NP liegen. Es kann also auch ein Optimierungsproblem oder sogar ein unentscheidbares Problem sein.&lt;br /&gt;
&lt;br /&gt;
== NP-vollständige Probleme ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem heißt &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;, wenn zwei Bedingungen erfüllt sind:&lt;br /&gt;
&lt;br /&gt;
# Das Problem liegt in NP.&lt;br /&gt;
# Das Problem ist NP-schwer.&lt;br /&gt;
&lt;br /&gt;
NP-vollständige Probleme sind die schwierigsten Probleme innerhalb von NP. Wenn man für ein NP-vollständiges Problem einen polynomialen Algorithmus finden würde, dann könnte jedes Problem aus NP in polynomialer Zeit gelöst werden.&lt;br /&gt;
&lt;br /&gt;
== P-vs.-NP-Problem ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;P-vs.-NP-Problem&#039;&#039;&#039; ist eines der bekanntesten offenen Probleme der Informatik und Mathematik.&lt;br /&gt;
&lt;br /&gt;
Es fragt:&lt;br /&gt;
&lt;br /&gt;
: Gilt &amp;lt;math&amp;gt;P = NP&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;P \neq NP&amp;lt;/math&amp;gt;?&lt;br /&gt;
&lt;br /&gt;
Anders formuliert:&lt;br /&gt;
&lt;br /&gt;
: Kann jedes Problem, dessen Lösung effizient überprüfbar ist, auch effizient gelöst werden?&lt;br /&gt;
&lt;br /&gt;
Bis heute ist nicht bekannt, ob &amp;lt;math&amp;gt;P = NP&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;P \neq NP&amp;lt;/math&amp;gt; gilt. Die meisten Fachleute vermuten, dass &amp;lt;math&amp;gt;P \neq NP&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
&lt;br /&gt;
== Reduktionen ==&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Reduktion&#039;&#039;&#039; ist eine Methode, um ein Problem auf ein anderes Problem zurückzuführen.&lt;br /&gt;
&lt;br /&gt;
Wenn ein Problem &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; auf ein Problem &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; reduziert werden kann, bedeutet das: Eine Lösung für &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; kann verwendet werden, um auch &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; zu lösen.&lt;br /&gt;
&lt;br /&gt;
Formal schreibt man häufig:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;A \leq_p B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; ist in polynomialer Zeit auf &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; reduzierbar.&lt;br /&gt;
&lt;br /&gt;
Reduktionen sind besonders wichtig, um NP-Schwere oder NP-Vollständigkeit zu zeigen.&lt;br /&gt;
&lt;br /&gt;
== Algorithmen ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Algorithmus&#039;&#039;&#039; ist eine eindeutige, endliche Handlungsvorschrift zur Lösung eines Problems.&lt;br /&gt;
&lt;br /&gt;
Ein Algorithmus sollte folgende Eigenschaften besitzen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Eigenschaft&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| Eindeutigkeit&lt;br /&gt;
| Jeder Schritt ist klar definiert&lt;br /&gt;
|-&lt;br /&gt;
| Endlichkeit&lt;br /&gt;
| Die Beschreibung des Algorithmus ist endlich&lt;br /&gt;
|-&lt;br /&gt;
| Ausführbarkeit&lt;br /&gt;
| Jeder Schritt kann tatsächlich ausgeführt werden&lt;br /&gt;
|-&lt;br /&gt;
| Terminierung&lt;br /&gt;
| Der Algorithmus endet nach endlich vielen Schritten&lt;br /&gt;
|-&lt;br /&gt;
| Korrektheit&lt;br /&gt;
| Der Algorithmus liefert das richtige Ergebnis&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der theoretischen Informatik werden Algorithmen nicht nur implementiert, sondern mathematisch analysiert.&lt;br /&gt;
&lt;br /&gt;
== Korrektheit von Algorithmen ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Korrektheit&#039;&#039;&#039; eines Algorithmus bedeutet, dass er für jede zulässige Eingabe das richtige Ergebnis liefert.&lt;br /&gt;
&lt;br /&gt;
Man unterscheidet:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;partielle Korrektheit&#039;&#039;&#039;,&lt;br /&gt;
* &#039;&#039;&#039;totale Korrektheit&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Partielle Korrektheit ===&lt;br /&gt;
&lt;br /&gt;
Ein Algorithmus ist partiell korrekt, wenn gilt:&lt;br /&gt;
&lt;br /&gt;
: Falls der Algorithmus terminiert, liefert er ein korrektes Ergebnis.&lt;br /&gt;
&lt;br /&gt;
Die partielle Korrektheit sagt also noch nichts darüber aus, ob der Algorithmus tatsächlich immer endet.&lt;br /&gt;
&lt;br /&gt;
=== Totale Korrektheit ===&lt;br /&gt;
&lt;br /&gt;
Ein Algorithmus ist total korrekt, wenn er partiell korrekt ist und zusätzlich für jede zulässige Eingabe terminiert.&lt;br /&gt;
&lt;br /&gt;
Totale Korrektheit umfasst also:&lt;br /&gt;
&lt;br /&gt;
* Korrektheit des Ergebnisses,&lt;br /&gt;
* Terminierung.&lt;br /&gt;
&lt;br /&gt;
== Beweismethoden in der theoretischen Informatik ==&lt;br /&gt;
&lt;br /&gt;
In der theoretischen Informatik spielen mathematische Beweise eine zentrale Rolle. Wichtige Beweismethoden sind:&lt;br /&gt;
&lt;br /&gt;
* direkter Beweis,&lt;br /&gt;
* Beweis durch Widerspruch,&lt;br /&gt;
* vollständige Induktion,&lt;br /&gt;
* strukturelle Induktion,&lt;br /&gt;
* Reduktionsbeweis,&lt;br /&gt;
* Pumping-Lemma-Beweis.&lt;br /&gt;
&lt;br /&gt;
== Vollständige Induktion ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;vollständige Induktion&#039;&#039;&#039; ist eine Beweismethode für Aussagen über natürliche Zahlen.&lt;br /&gt;
&lt;br /&gt;
Ein Induktionsbeweis besteht aus:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Induktionsanfang&#039;&#039;&#039;: Die Aussage wird für einen Startwert bewiesen, meist &amp;lt;math&amp;gt;n = 0&amp;lt;/math&amp;gt; oder &amp;lt;math&amp;gt;n = 1&amp;lt;/math&amp;gt;.&lt;br /&gt;
# &#039;&#039;&#039;Induktionsvoraussetzung&#039;&#039;&#039;: Man nimmt an, dass die Aussage für ein beliebiges &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; gilt.&lt;br /&gt;
# &#039;&#039;&#039;Induktionsschritt&#039;&#039;&#039;: Man zeigt, dass daraus die Aussage für &amp;lt;math&amp;gt;n+1&amp;lt;/math&amp;gt; folgt.&lt;br /&gt;
&lt;br /&gt;
Induktion wird häufig verwendet, um Eigenschaften von Algorithmen, Rekursionen oder formalen Sprachen zu beweisen.&lt;br /&gt;
&lt;br /&gt;
== Pumping-Lemma ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;Pumping-Lemma&#039;&#039;&#039; ist ein wichtiges Werkzeug, um zu zeigen, dass eine Sprache nicht regulär ist.&lt;br /&gt;
&lt;br /&gt;
Vereinfacht besagt es:&lt;br /&gt;
&lt;br /&gt;
: Wenn eine Sprache regulär ist, dann lassen sich hinreichend lange Wörter dieser Sprache in Teile zerlegen, sodass ein bestimmter Mittelteil beliebig oft wiederholt werden kann und das Wort trotzdem in der Sprache bleibt.&lt;br /&gt;
&lt;br /&gt;
Das Pumping-Lemma wird meist indirekt verwendet:&lt;br /&gt;
&lt;br /&gt;
# Man nimmt an, eine Sprache sei regulär.&lt;br /&gt;
# Dann müsste das Pumping-Lemma gelten.&lt;br /&gt;
# Man zeigt, dass dies zu einem Widerspruch führt.&lt;br /&gt;
# Also ist die Sprache nicht regulär.&lt;br /&gt;
&lt;br /&gt;
=== Beispielidee ===&lt;br /&gt;
&lt;br /&gt;
Die Sprache&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{a^n b^n \mid n \geq 0\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist nicht regulär.&lt;br /&gt;
&lt;br /&gt;
Ein endlicher Automat kann nicht beliebig viele &amp;lt;math&amp;gt;a&amp;lt;/math&amp;gt;-Zeichen zählen, um anschließend genau dieselbe Anzahl von &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt;-Zeichen zu überprüfen.&lt;br /&gt;
&lt;br /&gt;
== Aussagenlogik ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Aussagenlogik&#039;&#039;&#039; beschäftigt sich mit Aussagen, die entweder wahr oder falsch sind.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Aussagen:&lt;br /&gt;
&lt;br /&gt;
* „Es regnet.“&lt;br /&gt;
* „5 ist eine Primzahl.“&lt;br /&gt;
* „Ein Automat akzeptiert das Wort.“&lt;br /&gt;
&lt;br /&gt;
Wichtige logische Operatoren sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\neg&amp;lt;/math&amp;gt;&lt;br /&gt;
| Nicht&lt;br /&gt;
| &amp;lt;math&amp;gt;\neg A&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\land&amp;lt;/math&amp;gt;&lt;br /&gt;
| Und&lt;br /&gt;
| &amp;lt;math&amp;gt;A \land B&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\lor&amp;lt;/math&amp;gt;&lt;br /&gt;
| Oder&lt;br /&gt;
| &amp;lt;math&amp;gt;A \lor B&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\rightarrow&amp;lt;/math&amp;gt;&lt;br /&gt;
| Implikation&lt;br /&gt;
| &amp;lt;math&amp;gt;A \rightarrow B&amp;lt;/math&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\leftrightarrow&amp;lt;/math&amp;gt;&lt;br /&gt;
| Äquivalenz&lt;br /&gt;
| &amp;lt;math&amp;gt;A \leftrightarrow B&amp;lt;/math&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Aussagenlogik ist wichtig für Schaltalgebra, Verifikation, Datenbanken und formale Spezifikationen.&lt;br /&gt;
&lt;br /&gt;
== Prädikatenlogik ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Prädikatenlogik&#039;&#039;&#039; erweitert die Aussagenlogik um Variablen, Prädikate und Quantoren.&lt;br /&gt;
&lt;br /&gt;
Wichtige Quantoren sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\forall&amp;lt;/math&amp;gt;&lt;br /&gt;
| Für alle&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\exists&amp;lt;/math&amp;gt;&lt;br /&gt;
| Es existiert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\forall x \in \mathbb{N}: x + 0 = x&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Aussage bedeutet: Für alle natürlichen Zahlen &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gilt, dass &amp;lt;math&amp;gt;x + 0 = x&amp;lt;/math&amp;gt; ist.&lt;br /&gt;
&lt;br /&gt;
== Mengen, Relationen und Funktionen ==&lt;br /&gt;
&lt;br /&gt;
Mathematische Grundbegriffe wie Mengen, Relationen und Funktionen sind für die theoretische Informatik unverzichtbar.&lt;br /&gt;
&lt;br /&gt;
=== Mengen ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Menge&#039;&#039;&#039; ist eine Zusammenfassung von unterscheidbaren Objekten.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;A = \{1,2,3,4\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtige Mengenoperationen sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Operation&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;A \cup B&amp;lt;/math&amp;gt;&lt;br /&gt;
| Vereinigung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;A \cap B&amp;lt;/math&amp;gt;&lt;br /&gt;
| Schnittmenge&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;A \setminus B&amp;lt;/math&amp;gt;&lt;br /&gt;
| Differenz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;A \subseteq B&amp;lt;/math&amp;gt;&lt;br /&gt;
| Teilmenge&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Relationen ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Relation&#039;&#039;&#039; beschreibt eine Beziehung zwischen Elementen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;R = \{(1,2), (2,3), (3,4)\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Relationen sind wichtig für Graphen, Ordnungen, Äquivalenzklassen und Datenbanken.&lt;br /&gt;
&lt;br /&gt;
=== Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Eine &#039;&#039;&#039;Funktion&#039;&#039;&#039; ordnet jedem Element einer Definitionsmenge genau ein Element einer Zielmenge zu.&lt;br /&gt;
&lt;br /&gt;
Formal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;f : A \rightarrow B&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;f(x) = x^2&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Graphentheorie ==&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Graphentheorie&#039;&#039;&#039; ist ein wichtiges Hilfsmittel der theoretischen Informatik. Ein Graph besteht aus Knoten und Kanten.&lt;br /&gt;
&lt;br /&gt;
Formal wird ein Graph oft als&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;G = (V,E)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
geschrieben.&lt;br /&gt;
&lt;br /&gt;
Dabei ist:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Symbol&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;V&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der Knoten&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;E&amp;lt;/math&amp;gt;&lt;br /&gt;
| Menge der Kanten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Graphen werden verwendet, um Netzwerke, Abhängigkeiten, Zustandsräume, Verkehrsverbindungen oder Datenstrukturen zu modellieren.&lt;br /&gt;
&lt;br /&gt;
=== Gerichtete und ungerichtete Graphen ===&lt;br /&gt;
&lt;br /&gt;
Bei einem &#039;&#039;&#039;ungerichteten Graphen&#039;&#039;&#039; haben Kanten keine Richtung.&lt;br /&gt;
&lt;br /&gt;
Bei einem &#039;&#039;&#039;gerichteten Graphen&#039;&#039;&#039; haben Kanten eine Richtung. Eine gerichtete Kante von &amp;lt;math&amp;gt;u&amp;lt;/math&amp;gt; nach &amp;lt;math&amp;gt;v&amp;lt;/math&amp;gt; wird als &amp;lt;math&amp;gt;(u,v)&amp;lt;/math&amp;gt; geschrieben.&lt;br /&gt;
&lt;br /&gt;
=== Wichtige graphentheoretische Probleme ===&lt;br /&gt;
&lt;br /&gt;
* Gibt es einen Weg zwischen zwei Knoten?&lt;br /&gt;
* Was ist der kürzeste Weg?&lt;br /&gt;
* Enthält der Graph einen Kreis?&lt;br /&gt;
* Gibt es eine topologische Sortierung?&lt;br /&gt;
* Gibt es eine Hamiltonsche Rundreise?&lt;br /&gt;
* Ist der Graph färbbar?&lt;br /&gt;
&lt;br /&gt;
Einige dieser Probleme sind effizient lösbar, andere sind NP-vollständig.&lt;br /&gt;
&lt;br /&gt;
== Zusammenhang zwischen den Teilgebieten ==&lt;br /&gt;
&lt;br /&gt;
Die verschiedenen Gebiete der theoretischen Informatik bauen stark aufeinander auf.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Teilgebiet&lt;br /&gt;
! Zentrale Frage&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| Formale Sprachen&lt;br /&gt;
| Wie beschreibt man Mengen von Wörtern?&lt;br /&gt;
| Syntax einer Programmiersprache&lt;br /&gt;
|-&lt;br /&gt;
| Automatentheorie&lt;br /&gt;
| Welche Maschinen erkennen welche Sprachen?&lt;br /&gt;
| Endlicher Automat für ein Suchmuster&lt;br /&gt;
|-&lt;br /&gt;
| Berechenbarkeitstheorie&lt;br /&gt;
| Was ist überhaupt algorithmisch lösbar?&lt;br /&gt;
| Halteproblem&lt;br /&gt;
|-&lt;br /&gt;
| Komplexitätstheorie&lt;br /&gt;
| Wie effizient ist ein Problem lösbar?&lt;br /&gt;
| P, NP, NP-vollständig&lt;br /&gt;
|-&lt;br /&gt;
| Logik&lt;br /&gt;
| Wie formalisiert man Aussagen und Beweise?&lt;br /&gt;
| Programmverifikation&lt;br /&gt;
|-&lt;br /&gt;
| Graphentheorie&lt;br /&gt;
| Wie modelliert man Beziehungen und Strukturen?&lt;br /&gt;
| Netzwerke und Abhängigkeiten&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Von Sprache zu Automat ==&lt;br /&gt;
&lt;br /&gt;
Angenommen, es soll die Sprache aller binären Wörter erkannt werden, die auf &amp;lt;math&amp;gt;01&amp;lt;/math&amp;gt; enden.&lt;br /&gt;
&lt;br /&gt;
Das Alphabet lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;\Sigma = \{0,1\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Sprache ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;L = \{w \in \{0,1\}^* \mid w \text{ endet auf } 01\}&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Sprache ist regulär. Sie kann beschrieben werden durch den regulären Ausdruck:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;(0 \mid 1)^*01&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein endlicher Automat kann diese Sprache erkennen, indem er sich merkt, ob zuletzt eine 0 gelesen wurde und ob danach eine 1 folgt.&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Von Problem zu Komplexitätsklasse ==&lt;br /&gt;
&lt;br /&gt;
Betrachtet wird das Problem:&lt;br /&gt;
&lt;br /&gt;
: Gibt es in einem Graphen einen Weg von Knoten &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; zu Knoten &amp;lt;math&amp;gt;t&amp;lt;/math&amp;gt;?&lt;br /&gt;
&lt;br /&gt;
Dieses Problem ist ein Entscheidungsproblem. Es kann mit einer Breitensuche oder Tiefensuche effizient gelöst werden. Die Laufzeit ist linear in der Anzahl der Knoten und Kanten.&lt;br /&gt;
&lt;br /&gt;
Daher gehört das Problem zur Klasse P.&lt;br /&gt;
&lt;br /&gt;
Ein anderes Problem lautet:&lt;br /&gt;
&lt;br /&gt;
: Gibt es in einem Graphen einen Kreis, der jeden Knoten genau einmal besucht?&lt;br /&gt;
&lt;br /&gt;
Dies ist das Hamiltonkreisproblem. Es liegt in NP, weil eine vorgeschlagene Lösung effizient überprüft werden kann. Gleichzeitig ist es NP-vollständig, weil es zu den schwierigsten Problemen innerhalb von NP gehört.&lt;br /&gt;
&lt;br /&gt;
== Praktische Bedeutung ==&lt;br /&gt;
&lt;br /&gt;
Obwohl theoretische Informatik abstrakt wirkt, hat sie viele praktische Anwendungen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Bereich&lt;br /&gt;
! Bedeutung der theoretischen Informatik&lt;br /&gt;
|-&lt;br /&gt;
| Compilerbau&lt;br /&gt;
| Formale Grammatiken beschreiben die Syntax von Programmiersprachen&lt;br /&gt;
|-&lt;br /&gt;
| Datenbanken&lt;br /&gt;
| Relationen, Logik und Anfrageauswertung beruhen auf formalen Grundlagen&lt;br /&gt;
|-&lt;br /&gt;
| Kryptographie&lt;br /&gt;
| Sicherheit hängt oft von komplexitätstheoretisch schwierigen Problemen ab&lt;br /&gt;
|-&lt;br /&gt;
| Softwareverifikation&lt;br /&gt;
| Logik wird genutzt, um Programme formal zu prüfen&lt;br /&gt;
|-&lt;br /&gt;
| Netzwerke&lt;br /&gt;
| Graphen modellieren Verbindungen und Routing&lt;br /&gt;
|-&lt;br /&gt;
| Künstliche Intelligenz&lt;br /&gt;
| Suchprobleme und Optimierungsprobleme werden formal analysiert&lt;br /&gt;
|-&lt;br /&gt;
| Algorithmik&lt;br /&gt;
| Laufzeiten und Korrektheit werden mathematisch untersucht&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Typische Klausurfragen ==&lt;br /&gt;
&lt;br /&gt;
Typische Aufgaben in der theoretischen Informatik sind:&lt;br /&gt;
&lt;br /&gt;
* Geben Sie eine formale Grammatik für eine Sprache an.&lt;br /&gt;
* Konstruieren Sie einen endlichen Automaten für eine gegebene Sprache.&lt;br /&gt;
* Wandeln Sie einen NEA in einen DEA um.&lt;br /&gt;
* Zeigen Sie mit dem Pumping-Lemma, dass eine Sprache nicht regulär ist.&lt;br /&gt;
* Bestimmen Sie, ob eine Sprache regulär, kontextfrei oder entscheidbar ist.&lt;br /&gt;
* Analysieren Sie die Laufzeit eines Algorithmus.&lt;br /&gt;
* Ordnen Sie ein Problem einer Komplexitätsklasse zu.&lt;br /&gt;
* Zeigen Sie, dass ein Problem NP-schwer oder NP-vollständig ist.&lt;br /&gt;
* Beweisen Sie die Korrektheit eines Algorithmus.&lt;br /&gt;
* Entscheiden Sie, ob eine logische Formel erfüllbar ist.&lt;br /&gt;
&lt;br /&gt;
== Wichtige Begriffe ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Begriff&lt;br /&gt;
! Kurzdefinition&lt;br /&gt;
|-&lt;br /&gt;
| Alphabet&lt;br /&gt;
| Endliche Menge von Zeichen&lt;br /&gt;
|-&lt;br /&gt;
| Wort&lt;br /&gt;
| Endliche Folge von Zeichen&lt;br /&gt;
|-&lt;br /&gt;
| Sprache&lt;br /&gt;
| Menge von Wörtern&lt;br /&gt;
|-&lt;br /&gt;
| Grammatik&lt;br /&gt;
| Regelsystem zur Erzeugung einer Sprache&lt;br /&gt;
|-&lt;br /&gt;
| Automat&lt;br /&gt;
| Abstraktes Berechnungsmodell&lt;br /&gt;
|-&lt;br /&gt;
| Reguläre Sprache&lt;br /&gt;
| Sprache, die durch endliche Automaten erkannt wird&lt;br /&gt;
|-&lt;br /&gt;
| Kontextfreie Sprache&lt;br /&gt;
| Sprache, die durch Kellerautomaten erkannt wird&lt;br /&gt;
|-&lt;br /&gt;
| Turingmaschine&lt;br /&gt;
| Allgemeines mathematisches Modell der Berechnung&lt;br /&gt;
|-&lt;br /&gt;
| Entscheidbares Problem&lt;br /&gt;
| Problem, für das ein Algorithmus immer anhält und korrekt entscheidet&lt;br /&gt;
|-&lt;br /&gt;
| Unentscheidbares Problem&lt;br /&gt;
| Problem, für das kein allgemeiner Entscheidungsalgorithmus existiert&lt;br /&gt;
|-&lt;br /&gt;
| Komplexitätsklasse&lt;br /&gt;
| Menge von Problemen mit ähnlichem Ressourcenbedarf&lt;br /&gt;
|-&lt;br /&gt;
| P&lt;br /&gt;
| Klasse effizient entscheidbarer Probleme&lt;br /&gt;
|-&lt;br /&gt;
| NP&lt;br /&gt;
| Klasse effizient überprüfbarer Entscheidungsprobleme&lt;br /&gt;
|-&lt;br /&gt;
| NP-schwer&lt;br /&gt;
| Mindestens so schwer wie alle Probleme aus NP&lt;br /&gt;
|-&lt;br /&gt;
| NP-vollständig&lt;br /&gt;
| Problem ist sowohl in NP als auch NP-schwer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
Die theoretische Informatik untersucht die formalen Grundlagen von Berechnung und Information. Sie beschäftigt sich mit Sprachen, Automaten, Algorithmen, Berechenbarkeit, Komplexität und Logik. Ihre zentrale Bedeutung liegt darin, die Möglichkeiten und Grenzen algorithmischer Verfahren zu verstehen.&lt;br /&gt;
&lt;br /&gt;
Formale Sprachen und Automaten helfen dabei, syntaktische Strukturen und einfache Berechnungsmodelle zu beschreiben. Die Berechenbarkeitstheorie zeigt, welche Probleme grundsätzlich algorithmisch lösbar sind. Die Komplexitätstheorie untersucht, welche lösbaren Probleme effizient lösbar sind und welche vermutlich einen hohen Rechenaufwand benötigen. Logik, Mengenlehre und Graphentheorie liefern die mathematischen Werkzeuge, um diese Fragen präzise zu formulieren und zu beweisen.&lt;br /&gt;
&lt;br /&gt;
Damit ist die theoretische Informatik nicht nur ein abstraktes Grundlagenfach, sondern eine zentrale Basis für viele praktische Bereiche der Informatik.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Automatentheorie]]&lt;br /&gt;
* [[Formale Sprachen]]&lt;br /&gt;
* [[Reguläre Sprachen]]&lt;br /&gt;
* [[Kontextfreie Sprachen]]&lt;br /&gt;
* [[Turingmaschine]]&lt;br /&gt;
* [[Berechenbarkeit]]&lt;br /&gt;
* [[Halteproblem]]&lt;br /&gt;
* [[Komplexitätstheorie]]&lt;br /&gt;
* [[P]]&lt;br /&gt;
* [[NP]]&lt;br /&gt;
* [[NP-schwer]]&lt;br /&gt;
* [[NP-vollständig]]&lt;br /&gt;
* [[P versus NP]]&lt;br /&gt;
* [[Graphentheorie]]&lt;br /&gt;
* [[Aussagenlogik]]&lt;br /&gt;
* [[Prädikatenlogik]]&lt;br /&gt;
&lt;br /&gt;
== Kategorien ==&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Theoretische Informatik]]&lt;br /&gt;
[[Kategorie:Informatik]]&lt;br /&gt;
[[Kategorie:Formale Sprachen]]&lt;br /&gt;
[[Kategorie:Automatentheorie]]&lt;br /&gt;
[[Kategorie:Komplexitätstheorie]]&lt;br /&gt;
[[Kategorie:Berechenbarkeit]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Grundbegriffe_der_Optimierung&amp;diff=190</id>
		<title>Grundbegriffe der Optimierung</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Grundbegriffe_der_Optimierung&amp;diff=190"/>
		<updated>2026-04-30T09:58:59Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* P, NP, NP-schwer, NP-vollständig und P vs. NP */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Grundbegriffe der Optimierung =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Optimierung&#039;&#039;&#039; beschäftigt sich mit der systematischen Suche nach einer Lösung, welche eine gegebene Zielfunktion unter bestimmten Randbedingungen bestmöglich erfüllt. In der Informatik, Mathematik und im Operations Research bildet sie die Grundlage zahlreicher Verfahren zur Entscheidungsunterstützung, Planung und Steuerung technischer sowie wirtschaftlicher Systeme.&lt;br /&gt;
&lt;br /&gt;
== Globales und lokales Optimum ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Optimum&#039;&#039;&#039; bezeichnet den besten erreichbaren Wert einer Zielfunktion innerhalb des zulässigen Lösungsraums.&lt;br /&gt;
&lt;br /&gt;
=== Globales Optimum ===&lt;br /&gt;
Ein Punkt &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; ist ein &#039;&#039;&#039;globales Minimum&#039;&#039;&#039; einer Zielfunktion &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;, wenn gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \leq f(x) \quad \forall x \in X&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; der gesamte zulässige Lösungsraum.&lt;br /&gt;
&lt;br /&gt;
Analog liegt ein &#039;&#039;&#039;globales Maximum&#039;&#039;&#039; vor, wenn:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \geq f(x) \quad \forall x \in X&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das globale Optimum stellt somit die beste Lösung über den gesamten Suchraum dar.&lt;br /&gt;
&lt;br /&gt;
=== Lokales Optimum ===&lt;br /&gt;
Ein Punkt &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; ist ein &#039;&#039;&#039;lokales Minimum&#039;&#039;&#039;, wenn er innerhalb einer Umgebung &amp;lt;math&amp;gt;U(x^*)&amp;lt;/math&amp;gt; minimal ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \leq f(x) \quad \forall x \in U(x^*)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein lokales Optimum ist nur im direkten Umfeld optimal, nicht zwangsläufig jedoch global.&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung in der Praxis ===&lt;br /&gt;
Viele Optimierungsprobleme – insbesondere nichtlineare oder kombinatorische – besitzen mehrere lokale Optima. Die Herausforderung vieler Optimierungsverfahren besteht darin, das globale statt nur eines lokalen Optimums zu finden.&lt;br /&gt;
&lt;br /&gt;
== Umwandlung eines Maximierungsproblems in ein Minimierungsproblem und umgekehrt ==&lt;br /&gt;
&lt;br /&gt;
Da viele Optimierungsalgorithmen nur für Minimierungsprobleme formuliert sind, werden Maximierungsprobleme häufig umgeformt.&lt;br /&gt;
&lt;br /&gt;
=== Maximierung zu Minimierung ===&lt;br /&gt;
Ein Maximierungsproblem&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\max f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann umgeschrieben werden zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min -f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Maximierung einer Funktion entspricht somit der Minimierung ihres negativen Wertes.&lt;br /&gt;
&lt;br /&gt;
=== Minimierung zu Maximierung ===&lt;br /&gt;
Analog gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entspricht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\max -f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vorteil ===&lt;br /&gt;
Durch diese Transformation können Algorithmen einheitlich für einen Problemtyp implementiert werden.&lt;br /&gt;
&lt;br /&gt;
== Notationen ==&lt;br /&gt;
&lt;br /&gt;
In der Optimierung existieren standardisierte mathematische Notationen zur Beschreibung von Problemen.&lt;br /&gt;
&lt;br /&gt;
=== Allgemeine Form eines Optimierungsproblems ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; – Entscheidungsvariable bzw. Lösungsvektor  &lt;br /&gt;
* &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; – zulässiger Lösungsraum / Menge aller erlaubten Lösungen  &lt;br /&gt;
* &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt; – Zielfunktion  &lt;br /&gt;
&lt;br /&gt;
=== Weitere häufige Schreibweisen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Notation&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\arg\min_{x \in X} f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Liefert den Punkt, an dem das Minimum angenommen wird&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\arg\max_{x \in X} f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Liefert den Punkt, an dem das Maximum angenommen wird&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\min f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Minimaler Funktionswert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\max f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Maximaler Funktionswert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Supremum und Infimum ==&lt;br /&gt;
&lt;br /&gt;
Nicht jede Funktion nimmt ihr Maximum oder Minimum tatsächlich an. Zur Beschreibung solcher Fälle werden die Begriffe &#039;&#039;&#039;Supremum&#039;&#039;&#039; und &#039;&#039;&#039;Infimum&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Supremum ===&lt;br /&gt;
Das &#039;&#039;&#039;Supremum&#039;&#039;&#039; einer Menge ist ihre kleinste obere Schranke.&lt;br /&gt;
&lt;br /&gt;
Notation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\sup_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Supremum muss nicht selbst in der Menge enthalten sein.&lt;br /&gt;
&lt;br /&gt;
=== Infimum ===&lt;br /&gt;
Das &#039;&#039;&#039;Infimum&#039;&#039;&#039; ist die größte untere Schranke einer Menge.&lt;br /&gt;
&lt;br /&gt;
Notation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\inf_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch das Infimum muss nicht angenommen werden.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
Für die offene Menge &amp;lt;math&amp;gt;X=(0,1)&amp;lt;/math&amp;gt; gilt bei &amp;lt;math&amp;gt;f(x)=x&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\inf f(x)=0&amp;lt;/math&amp;gt;, aber kein Minimum vorhanden  &lt;br /&gt;
* &amp;lt;math&amp;gt;\sup f(x)=1&amp;lt;/math&amp;gt;, aber kein Maximum vorhanden  &lt;br /&gt;
&lt;br /&gt;
== Kontinuierliche, diskrete und kombinatorische Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Optimierungsprobleme lassen sich anhand ihres Lösungsraums klassifizieren.&lt;br /&gt;
&lt;br /&gt;
=== Kontinuierliche Optimierung ===&lt;br /&gt;
Bei der &#039;&#039;&#039;kontinuierlichen Optimierung&#039;&#039;&#039; können Entscheidungsvariablen beliebige reelle Werte innerhalb eines Bereichs annehmen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in \mathbb{R}^n&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typische Anwendungsfälle:&lt;br /&gt;
&lt;br /&gt;
* Parameteroptimierung  &lt;br /&gt;
* Regelungstechnik  &lt;br /&gt;
* Maschinelles Lernen  &lt;br /&gt;
&lt;br /&gt;
=== Diskrete Optimierung ===&lt;br /&gt;
Bei der &#039;&#039;&#039;diskreten Optimierung&#039;&#039;&#039; dürfen Variablen nur diskrete Werte annehmen, beispielsweise ganze Zahlen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in \mathbb{Z}^n&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typische Anwendungsfälle:&lt;br /&gt;
&lt;br /&gt;
* Produktionsplanung  &lt;br /&gt;
* Ressourcenallokation  &lt;br /&gt;
* Ganzzahlige lineare Optimierung  &lt;br /&gt;
&lt;br /&gt;
=== Kombinatorische Optimierung ===&lt;br /&gt;
Die &#039;&#039;&#039;kombinatorische Optimierung&#039;&#039;&#039; ist ein Spezialfall der diskreten Optimierung, bei dem aus einer endlichen Menge diskreter Kombinationen die beste ausgewählt wird.&lt;br /&gt;
&lt;br /&gt;
Typische Probleme:&lt;br /&gt;
&lt;br /&gt;
* Traveling Salesman Problem  &lt;br /&gt;
* Rucksackproblem  &lt;br /&gt;
* Graphfärbung  &lt;br /&gt;
* Scheduling-Probleme  &lt;br /&gt;
&lt;br /&gt;
=== Vergleich ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ&lt;br /&gt;
! Lösungsraum&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| Kontinuierlich&lt;br /&gt;
| Reelle Zahlen&lt;br /&gt;
| Parameteroptimierung&lt;br /&gt;
|-&lt;br /&gt;
| Diskret&lt;br /&gt;
| Ganze Zahlen / diskrete Werte&lt;br /&gt;
| Ganzzahlige Programmierung&lt;br /&gt;
|-&lt;br /&gt;
| Kombinatorisch&lt;br /&gt;
| Endliche Kombinationen / Strukturen&lt;br /&gt;
| TSP, Rucksackproblem&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
Die Grundbegriffe der Optimierung bilden das Fundament für das Verständnis moderner Optimierungsverfahren. Insbesondere die Unterscheidung zwischen lokalen und globalen Optima, die mathematische Notation von Optimierungsproblemen sowie die Klassifikation verschiedener Problemtypen sind essenziell für die Auswahl geeigneter Lösungsalgorithmen.&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=P,_NP,_NP-schwer,_NP-vollst%C3%A4ndig_und_P_vs._NP&amp;diff=189</id>
		<title>P, NP, NP-schwer, NP-vollständig und P vs. NP</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=P,_NP,_NP-schwer,_NP-vollst%C3%A4ndig_und_P_vs._NP&amp;diff=189"/>
		<updated>2026-04-30T09:58:54Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: Die Seite wurde neu angelegt: „= P, NP, NP-schwer, NP-vollständig und P vs. NP =  == Überblick ==  Die Begriffe &amp;#039;&amp;#039;&amp;#039;P&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NP&amp;#039;&amp;#039;&amp;#039;, &amp;#039;&amp;#039;&amp;#039;NP-schwer&amp;#039;&amp;#039;&amp;#039; und &amp;#039;&amp;#039;&amp;#039;NP-vollständig&amp;#039;&amp;#039;&amp;#039; stammen aus der theoretischen Informatik und dienen dazu, Probleme nach ihrer algorithmischen Schwierigkeit einzuordnen. Sie beschreiben nicht einzelne Programme, sondern Klassen von Problemen.  Besonders wichtig sind diese Begriffe bei Optimierungsproblemen, da viele praktische Probleme nicht effizient exakt lös…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= P, NP, NP-schwer, NP-vollständig und P vs. NP =&lt;br /&gt;
&lt;br /&gt;
== Überblick ==&lt;br /&gt;
&lt;br /&gt;
Die Begriffe &#039;&#039;&#039;P&#039;&#039;&#039;, &#039;&#039;&#039;NP&#039;&#039;&#039;, &#039;&#039;&#039;NP-schwer&#039;&#039;&#039; und &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039; stammen aus der theoretischen Informatik und dienen dazu, Probleme nach ihrer algorithmischen Schwierigkeit einzuordnen. Sie beschreiben nicht einzelne Programme, sondern Klassen von Problemen.&lt;br /&gt;
&lt;br /&gt;
Besonders wichtig sind diese Begriffe bei Optimierungsproblemen, da viele praktische Probleme nicht effizient exakt lösbar sind und daher heuristische oder approximative Verfahren eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== P ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;P&#039;&#039;&#039; steht für &#039;&#039;polynomial time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein Entscheidungsproblem liegt in der Klasse &#039;&#039;&#039;P&#039;&#039;&#039;, wenn es mit einem deterministischen Algorithmus in polynomialer Zeit gelöst werden kann.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Die Laufzeit wächst höchstens polynomial mit der Eingabegröße, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
O(n),\ O(n^2),\ O(n^3)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Probleme in P gelten als effizient lösbar.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Die Frage, ob eine Zahl gerade ist, kann sehr schnell entschieden werden. Auch viele Sortier- und Suchprobleme liegen in P.&lt;br /&gt;
&lt;br /&gt;
== NP ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NP&#039;&#039;&#039; steht für &#039;&#039;nondeterministic polynomial time&#039;&#039;, auf Deutsch etwa: &#039;&#039;&#039;nichtdeterministisch polynomiell&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein Entscheidungsproblem liegt in &#039;&#039;&#039;NP&#039;&#039;&#039;, wenn eine gegebene Lösung in polynomialer Zeit überprüft werden kann.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist:&lt;br /&gt;
&lt;br /&gt;
* Die Lösung muss nicht schnell gefunden werden können.&lt;br /&gt;
* Aber wenn eine mögliche Lösung gegeben ist, kann sie schnell überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Beim Traveling-Salesman-Problem als Entscheidungsproblem lautet die Frage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gibt es eine Rundreise mit einer Länge von höchstens 100 km?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine konkrete Route gegeben ist, kann man ihre Länge leicht berechnen und prüfen, ob sie höchstens 100 km beträgt. Daher liegt dieses Entscheidungsproblem in NP.&lt;br /&gt;
&lt;br /&gt;
== NP-schwer ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem ist &#039;&#039;&#039;NP-schwer&#039;&#039;&#039; beziehungsweise &#039;&#039;NP-hard&#039;&#039;, wenn jedes Problem aus NP in polynomialer Zeit auf dieses Problem reduziert werden kann.&lt;br /&gt;
&lt;br /&gt;
Anschaulich bedeutet das:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Ein NP-schweres Problem ist mindestens so schwierig wie die schwierigsten Probleme aus NP.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist: Ein NP-schweres Problem muss selbst nicht in NP liegen. Es kann also auch ein Optimierungsproblem sein und muss nicht zwingend ein Entscheidungsproblem sein.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Das Optimierungsproblem des Traveling Salesman Problems lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Finde die kürzeste Rundreise durch alle Städte.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Optimierungsproblem ist NP-schwer.&lt;br /&gt;
&lt;br /&gt;
== NP-vollständig ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem ist &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;, wenn zwei Bedingungen erfüllt sind:&lt;br /&gt;
&lt;br /&gt;
# Das Problem liegt in NP.&lt;br /&gt;
# Das Problem ist NP-schwer.&lt;br /&gt;
&lt;br /&gt;
Formal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\text{NP-vollständig} = \text{NP} \cap \text{NP-schwer}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NP-vollständige Probleme sind also die schwierigsten Probleme innerhalb der Klasse NP.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Das Entscheidungsproblem des Traveling Salesman Problems:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gibt es eine Rundreise mit einer Länge von höchstens K?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist NP-vollständig.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen NP-schwer und NP-vollständig ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Begriff&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Muss in NP liegen?&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP&#039;&#039;&#039;&lt;br /&gt;
| Lösungen sind schnell überprüfbar&lt;br /&gt;
| Ja&lt;br /&gt;
| TSP als Entscheidungsproblem&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;&lt;br /&gt;
| Mindestens so schwer wie alle Probleme in NP&lt;br /&gt;
| Nein&lt;br /&gt;
| TSP als Optimierungsproblem&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;&lt;br /&gt;
| In NP und zugleich NP-schwer&lt;br /&gt;
| Ja&lt;br /&gt;
| TSP als Entscheidungsproblem&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== P vs. NP ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;P-vs.-NP-Problem&#039;&#039;&#039; ist eines der bekanntesten offenen Probleme der Informatik.&lt;br /&gt;
&lt;br /&gt;
Die zentrale Frage lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gilt P = NP?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder anders formuliert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Kann jedes Problem, dessen Lösung schnell überprüft werden kann, auch schnell gelöst werden?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bedeutung von P = NP ==&lt;br /&gt;
&lt;br /&gt;
Falls &#039;&#039;&#039;P = NP&#039;&#039;&#039; gelten würde, dann könnten alle Probleme in NP effizient gelöst werden. Das hätte weitreichende Folgen, zum Beispiel für:&lt;br /&gt;
&lt;br /&gt;
* Optimierung&lt;br /&gt;
* Kryptographie&lt;br /&gt;
* Logistik&lt;br /&gt;
* Planung&lt;br /&gt;
* Künstliche Intelligenz&lt;br /&gt;
&lt;br /&gt;
Viele heute als schwer geltende Probleme könnten dann effizient gelöst werden.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung von P ≠ NP ==&lt;br /&gt;
&lt;br /&gt;
Die meisten Fachleute vermuten, dass gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P \neq NP&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das würde bedeuten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Es gibt Probleme, deren Lösungen schnell überprüft, aber nicht schnell gefunden werden können.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies erklärt, warum für viele schwierige Optimierungsprobleme keine effizienten exakten Algorithmen bekannt sind.&lt;br /&gt;
&lt;br /&gt;
== Grafische Einordnung ==&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;P \neq NP&amp;lt;/math&amp;gt; gilt, ergibt sich folgende typische Einordnung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Probleme&lt;br /&gt;
├── P&lt;br /&gt;
│   └── effizient lösbare Probleme&lt;br /&gt;
│&lt;br /&gt;
├── NP&lt;br /&gt;
│   ├── P&lt;br /&gt;
│   └── NP-vollständige Probleme&lt;br /&gt;
│&lt;br /&gt;
└── NP-schwere Probleme&lt;br /&gt;
    ├── NP-vollständige Probleme&lt;br /&gt;
    └── weitere, nicht zwingend in NP liegende Probleme&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relevanz für Optimierungsprobleme ==&lt;br /&gt;
&lt;br /&gt;
Viele praktische Optimierungsprobleme sind NP-schwer. Dazu gehören beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Routenplanung&lt;br /&gt;
* Produktionsplanung&lt;br /&gt;
* Scheduling&lt;br /&gt;
* Rucksackproblem&lt;br /&gt;
* Zuordnungsprobleme&lt;br /&gt;
* viele gemischt-ganzzahlige Optimierungsprobleme&lt;br /&gt;
&lt;br /&gt;
Da exakte Verfahren bei großen Instanzen oft zu langsam sind, werden in der Praxis häufig approximative Verfahren eingesetzt, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Heuristiken&lt;br /&gt;
* Metaheuristiken&lt;br /&gt;
* Greedy-Verfahren&lt;br /&gt;
* Branch-and-Bound mit Abbruchkriterien&lt;br /&gt;
* evolutionäre Algorithmen&lt;br /&gt;
&lt;br /&gt;
== Merksätze ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;: Probleme sind schnell lösbar.&lt;br /&gt;
* &#039;&#039;&#039;NP&#039;&#039;&#039;: Lösungen sind schnell überprüfbar.&lt;br /&gt;
* &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;: Mindestens so schwer wie alle Probleme in NP.&lt;br /&gt;
* &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;: In NP und NP-schwer.&lt;br /&gt;
* &#039;&#039;&#039;P vs. NP&#039;&#039;&#039;: Offene Frage, ob schnelles Überprüfen auch schnelles Lösen bedeutet.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Heuristische Optimierungsverfahren]]&lt;br /&gt;
* [[Metaheuristische Verfahren]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=188</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=188"/>
		<updated>2026-04-30T09:58:40Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Theoretische Informatik */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Grundlagen der Theoretischen Informatik]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=187</id>
		<title>Hauptseite</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Hauptseite&amp;diff=187"/>
		<updated>2026-04-30T09:58:09Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Optimierungsverfahren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
== Uni ==&lt;br /&gt;
&lt;br /&gt;
=== Verteilte Informationsverarbeitung ===&lt;br /&gt;
* [[Verteiltes System]]&lt;br /&gt;
** [[Zustandsloser Server]]&lt;br /&gt;
* [[Netzwerkprotokoll]]&lt;br /&gt;
** [[OSI-Modell]]&lt;br /&gt;
** [[Transportprotokolle]]&lt;br /&gt;
*** [[TCP/IP-Modell]]&lt;br /&gt;
*** [[TCP]]&lt;br /&gt;
*** [[UDP]]&lt;br /&gt;
** [[Network File System (NFS)]]&lt;br /&gt;
* [[Entfernte Funktionsaufrufe (RPC)]]&lt;br /&gt;
** [[XDR]] (External Data Representation)&lt;br /&gt;
** [[Opaker Datentyp]]&lt;br /&gt;
&lt;br /&gt;
==== Programmierung für verteilte Systeme ====&lt;br /&gt;
* [[ Programmierung für verteilte Systeme]]&lt;br /&gt;
* [[RabbitMQ]]&lt;br /&gt;
* [[Advanced Message Queuing Protocol (AMQP)]]&lt;br /&gt;
&lt;br /&gt;
== Programmierung ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemein ===&lt;br /&gt;
* [[Semantic Versioning]]&lt;br /&gt;
&lt;br /&gt;
=== Software Entwurfsmuster ===&lt;br /&gt;
* [[Entwurfsmuster (Softwareentwicklung)]]&lt;br /&gt;
* [[Reactor-Entwurfsmuster (Reactor Pattern)]]&lt;br /&gt;
* [[Proactor-Entwurfsmuster (Proactor Pattern)]]&lt;br /&gt;
* [[Observer-Entwurfsmuster (Observer Pattern)]]&lt;br /&gt;
* [[Competing-Consumer-Entwurfsmuster]]&lt;br /&gt;
* [[Mediator-Entwurfsmuster]]&lt;br /&gt;
* [[Publisher-Subscriber-Entwurfsmuster]]&lt;br /&gt;
* [[Messaging Pattern]]&lt;br /&gt;
&lt;br /&gt;
=== Software Architecture ===&lt;br /&gt;
* [[Event-Driven Architecture (EDA)]]&lt;br /&gt;
&lt;br /&gt;
=== GUI Entwurfsmuster ===&lt;br /&gt;
* [[Vergleich der GUI-Architekturmuster MVC, MVP und MVVM]]&lt;br /&gt;
* [[Model-View-Controller (MVC)]]&lt;br /&gt;
* [[Model-View-Presenter (MVP)]]&lt;br /&gt;
* [[Model-View-ViewModel (MVVM)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenrepräsentation ===&lt;br /&gt;
* [[Datenrepräsentation]]&lt;br /&gt;
* [[eXtensible Markup Language (XML)]]&lt;br /&gt;
* [[JavaScript Object Notation (JSON)]]&lt;br /&gt;
* [[Google Protocol Buffers (Protobuf)]]&lt;br /&gt;
&lt;br /&gt;
=== Deployment ===&lt;br /&gt;
* [[Feature Flags]]&lt;br /&gt;
&lt;br /&gt;
=== Unix ===&lt;br /&gt;
* [[POSIX Threads (pthreads)]]&lt;br /&gt;
* [[Unix Domain Sockets]]&lt;br /&gt;
* [[POSIX Message Queues]]&lt;br /&gt;
&lt;br /&gt;
== Netzwerk Architektur ==&lt;br /&gt;
* [[Client-Server-Architektur]]&lt;br /&gt;
* [[Peer-to-Peer-Netzwerke (P2P)]]&lt;br /&gt;
** [[Kademlia-Netzwerk]]&lt;br /&gt;
* [[Microservices-Architektur]]&lt;br /&gt;
* [[Service-Oriented Architecture (SOA)]]&lt;br /&gt;
&lt;br /&gt;
=== Netzwerk Programmierung ===&lt;br /&gt;
* [[Remote Procedure Calls (RPC)]]&lt;br /&gt;
** [[rpcgen]]&lt;br /&gt;
*** [[rpcgen – Interne Funktionsweise]]&lt;br /&gt;
** [[XDR (External Data Representation)]]&lt;br /&gt;
&lt;br /&gt;
== Programmier Sprachen ==&lt;br /&gt;
&lt;br /&gt;
=== C ===&lt;br /&gt;
* [[Main Funktion]]&lt;br /&gt;
* [[Sockets]]&lt;br /&gt;
* [[malloc]]&lt;br /&gt;
&lt;br /&gt;
==== Datentypen ====&lt;br /&gt;
* [[Datentypen in C]]&lt;br /&gt;
* [[uint8_t (C-Datentyp)]]&lt;br /&gt;
* [[memset]]&lt;br /&gt;
* [[Pointer]]&lt;br /&gt;
* [[Strings in C]]&lt;br /&gt;
&lt;br /&gt;
=== C++ ===&lt;br /&gt;
* [[Scope Resolution Operator (::) in C++]]&lt;br /&gt;
&lt;br /&gt;
=== C# ===&lt;br /&gt;
&lt;br /&gt;
* -&lt;br /&gt;
&lt;br /&gt;
== Theoretische Informatik ==&lt;br /&gt;
* [[Theoretische Informatik]]&lt;br /&gt;
* [[P, NP, NP-schwer, NP-vollständig und P vs. NP]]&lt;br /&gt;
&lt;br /&gt;
== Optimierungsverfahren ==&lt;br /&gt;
* [[Grundbegriffe der Optimierung]]&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Lineare Programmierung]]&lt;br /&gt;
* [[Single Objective Rankings]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;br /&gt;
* [[Random Forest]]&lt;br /&gt;
* [[Pareto-Optimierung]]&lt;br /&gt;
* ---&lt;br /&gt;
* [[Optimierungsverfahren Glossar]]&lt;br /&gt;
&lt;br /&gt;
== Wiki ==&lt;br /&gt;
*[[SyntaxHighlight]]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Formatting Formatting]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Images Bilder]&lt;br /&gt;
*[https://en.wikipedia.org/wiki/Help:Line-break_handling Line-break handling]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Help:Tables/de Tabellen]&lt;br /&gt;
*[https://de.wikipedia.org/wiki/Hilfe:Tabellen_f%C3%BCr_Fortgeschrittene#Senkrechter_Strich_als_Zelleninhalt Senkrechter Strich als Zelleninhalt]&lt;br /&gt;
*[https://www.mediawiki.org/wiki/Extension:Math Math Extension aktivieren]&lt;br /&gt;
&lt;br /&gt;
== Starthilfen ==&lt;br /&gt;
&lt;br /&gt;
Hilfe zur Verwendung und Konfiguration der Wiki-Software findest du im [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Benutzerhandbuch].&lt;br /&gt;
&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste der Konfigurationsparameter]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Häufige Fragen zu MediaWiki]&lt;br /&gt;
* [https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/ Mailingliste zu neuen Versionen von MediaWiki]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Übersetze MediaWiki für deine Sprache]&lt;br /&gt;
* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Erfahre, wie du Spam auf deinem Wiki bekämpfen kannst]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
	<entry>
		<id>http://dev.kaibel.net/index.php?title=Grundbegriffe_der_Optimierung&amp;diff=186</id>
		<title>Grundbegriffe der Optimierung</title>
		<link rel="alternate" type="text/html" href="http://dev.kaibel.net/index.php?title=Grundbegriffe_der_Optimierung&amp;diff=186"/>
		<updated>2026-04-30T09:55:32Z</updated>

		<summary type="html">&lt;p&gt;PhilKa: /* Zusammenfassung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Grundbegriffe der Optimierung =&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;Optimierung&#039;&#039;&#039; beschäftigt sich mit der systematischen Suche nach einer Lösung, welche eine gegebene Zielfunktion unter bestimmten Randbedingungen bestmöglich erfüllt. In der Informatik, Mathematik und im Operations Research bildet sie die Grundlage zahlreicher Verfahren zur Entscheidungsunterstützung, Planung und Steuerung technischer sowie wirtschaftlicher Systeme.&lt;br /&gt;
&lt;br /&gt;
== Globales und lokales Optimum ==&lt;br /&gt;
&lt;br /&gt;
Ein &#039;&#039;&#039;Optimum&#039;&#039;&#039; bezeichnet den besten erreichbaren Wert einer Zielfunktion innerhalb des zulässigen Lösungsraums.&lt;br /&gt;
&lt;br /&gt;
=== Globales Optimum ===&lt;br /&gt;
Ein Punkt &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; ist ein &#039;&#039;&#039;globales Minimum&#039;&#039;&#039; einer Zielfunktion &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt;, wenn gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \leq f(x) \quad \forall x \in X&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; der gesamte zulässige Lösungsraum.&lt;br /&gt;
&lt;br /&gt;
Analog liegt ein &#039;&#039;&#039;globales Maximum&#039;&#039;&#039; vor, wenn:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \geq f(x) \quad \forall x \in X&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das globale Optimum stellt somit die beste Lösung über den gesamten Suchraum dar.&lt;br /&gt;
&lt;br /&gt;
=== Lokales Optimum ===&lt;br /&gt;
Ein Punkt &amp;lt;math&amp;gt;x^*&amp;lt;/math&amp;gt; ist ein &#039;&#039;&#039;lokales Minimum&#039;&#039;&#039;, wenn er innerhalb einer Umgebung &amp;lt;math&amp;gt;U(x^*)&amp;lt;/math&amp;gt; minimal ist:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
f(x^*) \leq f(x) \quad \forall x \in U(x^*)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein lokales Optimum ist nur im direkten Umfeld optimal, nicht zwangsläufig jedoch global.&lt;br /&gt;
&lt;br /&gt;
=== Bedeutung in der Praxis ===&lt;br /&gt;
Viele Optimierungsprobleme – insbesondere nichtlineare oder kombinatorische – besitzen mehrere lokale Optima. Die Herausforderung vieler Optimierungsverfahren besteht darin, das globale statt nur eines lokalen Optimums zu finden.&lt;br /&gt;
&lt;br /&gt;
== Umwandlung eines Maximierungsproblems in ein Minimierungsproblem und umgekehrt ==&lt;br /&gt;
&lt;br /&gt;
Da viele Optimierungsalgorithmen nur für Minimierungsprobleme formuliert sind, werden Maximierungsprobleme häufig umgeformt.&lt;br /&gt;
&lt;br /&gt;
=== Maximierung zu Minimierung ===&lt;br /&gt;
Ein Maximierungsproblem&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\max f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann umgeschrieben werden zu&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min -f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Maximierung einer Funktion entspricht somit der Minimierung ihres negativen Wertes.&lt;br /&gt;
&lt;br /&gt;
=== Minimierung zu Maximierung ===&lt;br /&gt;
Analog gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entspricht&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\max -f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Vorteil ===&lt;br /&gt;
Durch diese Transformation können Algorithmen einheitlich für einen Problemtyp implementiert werden.&lt;br /&gt;
&lt;br /&gt;
== Notationen ==&lt;br /&gt;
&lt;br /&gt;
In der Optimierung existieren standardisierte mathematische Notationen zur Beschreibung von Problemen.&lt;br /&gt;
&lt;br /&gt;
=== Allgemeine Form eines Optimierungsproblems ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\min_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; – Entscheidungsvariable bzw. Lösungsvektor  &lt;br /&gt;
* &amp;lt;math&amp;gt;X&amp;lt;/math&amp;gt; – zulässiger Lösungsraum / Menge aller erlaubten Lösungen  &lt;br /&gt;
* &amp;lt;math&amp;gt;f(x)&amp;lt;/math&amp;gt; – Zielfunktion  &lt;br /&gt;
&lt;br /&gt;
=== Weitere häufige Schreibweisen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Notation&lt;br /&gt;
! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\arg\min_{x \in X} f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Liefert den Punkt, an dem das Minimum angenommen wird&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\arg\max_{x \in X} f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Liefert den Punkt, an dem das Maximum angenommen wird&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\min f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Minimaler Funktionswert&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;math&amp;gt;\max f(x)&amp;lt;/math&amp;gt;&lt;br /&gt;
| Maximaler Funktionswert&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Supremum und Infimum ==&lt;br /&gt;
&lt;br /&gt;
Nicht jede Funktion nimmt ihr Maximum oder Minimum tatsächlich an. Zur Beschreibung solcher Fälle werden die Begriffe &#039;&#039;&#039;Supremum&#039;&#039;&#039; und &#039;&#039;&#039;Infimum&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
=== Supremum ===&lt;br /&gt;
Das &#039;&#039;&#039;Supremum&#039;&#039;&#039; einer Menge ist ihre kleinste obere Schranke.&lt;br /&gt;
&lt;br /&gt;
Notation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\sup_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Supremum muss nicht selbst in der Menge enthalten sein.&lt;br /&gt;
&lt;br /&gt;
=== Infimum ===&lt;br /&gt;
Das &#039;&#039;&#039;Infimum&#039;&#039;&#039; ist die größte untere Schranke einer Menge.&lt;br /&gt;
&lt;br /&gt;
Notation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\inf_{x \in X} f(x)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch das Infimum muss nicht angenommen werden.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
Für die offene Menge &amp;lt;math&amp;gt;X=(0,1)&amp;lt;/math&amp;gt; gilt bei &amp;lt;math&amp;gt;f(x)=x&amp;lt;/math&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;math&amp;gt;\inf f(x)=0&amp;lt;/math&amp;gt;, aber kein Minimum vorhanden  &lt;br /&gt;
* &amp;lt;math&amp;gt;\sup f(x)=1&amp;lt;/math&amp;gt;, aber kein Maximum vorhanden  &lt;br /&gt;
&lt;br /&gt;
== Kontinuierliche, diskrete und kombinatorische Optimierung ==&lt;br /&gt;
&lt;br /&gt;
Optimierungsprobleme lassen sich anhand ihres Lösungsraums klassifizieren.&lt;br /&gt;
&lt;br /&gt;
=== Kontinuierliche Optimierung ===&lt;br /&gt;
Bei der &#039;&#039;&#039;kontinuierlichen Optimierung&#039;&#039;&#039; können Entscheidungsvariablen beliebige reelle Werte innerhalb eines Bereichs annehmen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in \mathbb{R}^n&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typische Anwendungsfälle:&lt;br /&gt;
&lt;br /&gt;
* Parameteroptimierung  &lt;br /&gt;
* Regelungstechnik  &lt;br /&gt;
* Maschinelles Lernen  &lt;br /&gt;
&lt;br /&gt;
=== Diskrete Optimierung ===&lt;br /&gt;
Bei der &#039;&#039;&#039;diskreten Optimierung&#039;&#039;&#039; dürfen Variablen nur diskrete Werte annehmen, beispielsweise ganze Zahlen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
x \in \mathbb{Z}^n&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typische Anwendungsfälle:&lt;br /&gt;
&lt;br /&gt;
* Produktionsplanung  &lt;br /&gt;
* Ressourcenallokation  &lt;br /&gt;
* Ganzzahlige lineare Optimierung  &lt;br /&gt;
&lt;br /&gt;
=== Kombinatorische Optimierung ===&lt;br /&gt;
Die &#039;&#039;&#039;kombinatorische Optimierung&#039;&#039;&#039; ist ein Spezialfall der diskreten Optimierung, bei dem aus einer endlichen Menge diskreter Kombinationen die beste ausgewählt wird.&lt;br /&gt;
&lt;br /&gt;
Typische Probleme:&lt;br /&gt;
&lt;br /&gt;
* Traveling Salesman Problem  &lt;br /&gt;
* Rucksackproblem  &lt;br /&gt;
* Graphfärbung  &lt;br /&gt;
* Scheduling-Probleme  &lt;br /&gt;
&lt;br /&gt;
=== Vergleich ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ&lt;br /&gt;
! Lösungsraum&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| Kontinuierlich&lt;br /&gt;
| Reelle Zahlen&lt;br /&gt;
| Parameteroptimierung&lt;br /&gt;
|-&lt;br /&gt;
| Diskret&lt;br /&gt;
| Ganze Zahlen / diskrete Werte&lt;br /&gt;
| Ganzzahlige Programmierung&lt;br /&gt;
|-&lt;br /&gt;
| Kombinatorisch&lt;br /&gt;
| Endliche Kombinationen / Strukturen&lt;br /&gt;
| TSP, Rucksackproblem&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung ==&lt;br /&gt;
&lt;br /&gt;
Die Grundbegriffe der Optimierung bilden das Fundament für das Verständnis moderner Optimierungsverfahren. Insbesondere die Unterscheidung zwischen lokalen und globalen Optima, die mathematische Notation von Optimierungsproblemen sowie die Klassifikation verschiedener Problemtypen sind essenziell für die Auswahl geeigneter Lösungsalgorithmen.&lt;br /&gt;
&lt;br /&gt;
= P, NP, NP-schwer, NP-vollständig und P vs. NP =&lt;br /&gt;
&lt;br /&gt;
== Überblick ==&lt;br /&gt;
&lt;br /&gt;
Die Begriffe &#039;&#039;&#039;P&#039;&#039;&#039;, &#039;&#039;&#039;NP&#039;&#039;&#039;, &#039;&#039;&#039;NP-schwer&#039;&#039;&#039; und &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039; stammen aus der theoretischen Informatik und dienen dazu, Probleme nach ihrer algorithmischen Schwierigkeit einzuordnen. Sie beschreiben nicht einzelne Programme, sondern Klassen von Problemen.&lt;br /&gt;
&lt;br /&gt;
Besonders wichtig sind diese Begriffe bei Optimierungsproblemen, da viele praktische Probleme nicht effizient exakt lösbar sind und daher heuristische oder approximative Verfahren eingesetzt werden.&lt;br /&gt;
&lt;br /&gt;
== P ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;P&#039;&#039;&#039; steht für &#039;&#039;polynomial time&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein Entscheidungsproblem liegt in der Klasse &#039;&#039;&#039;P&#039;&#039;&#039;, wenn es mit einem deterministischen Algorithmus in polynomialer Zeit gelöst werden kann.&lt;br /&gt;
&lt;br /&gt;
Das bedeutet: Die Laufzeit wächst höchstens polynomial mit der Eingabegröße, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
O(n),\ O(n^2),\ O(n^3)&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Probleme in P gelten als effizient lösbar.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Die Frage, ob eine Zahl gerade ist, kann sehr schnell entschieden werden. Auch viele Sortier- und Suchprobleme liegen in P.&lt;br /&gt;
&lt;br /&gt;
== NP ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NP&#039;&#039;&#039; steht für &#039;&#039;nondeterministic polynomial time&#039;&#039;, auf Deutsch etwa: &#039;&#039;&#039;nichtdeterministisch polynomiell&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Ein Entscheidungsproblem liegt in &#039;&#039;&#039;NP&#039;&#039;&#039;, wenn eine gegebene Lösung in polynomialer Zeit überprüft werden kann.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist:&lt;br /&gt;
&lt;br /&gt;
* Die Lösung muss nicht schnell gefunden werden können.&lt;br /&gt;
* Aber wenn eine mögliche Lösung gegeben ist, kann sie schnell überprüft werden.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Beim Traveling-Salesman-Problem als Entscheidungsproblem lautet die Frage:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gibt es eine Rundreise mit einer Länge von höchstens 100 km?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine konkrete Route gegeben ist, kann man ihre Länge leicht berechnen und prüfen, ob sie höchstens 100 km beträgt. Daher liegt dieses Entscheidungsproblem in NP.&lt;br /&gt;
&lt;br /&gt;
== NP-schwer ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem ist &#039;&#039;&#039;NP-schwer&#039;&#039;&#039; beziehungsweise &#039;&#039;NP-hard&#039;&#039;, wenn jedes Problem aus NP in polynomialer Zeit auf dieses Problem reduziert werden kann.&lt;br /&gt;
&lt;br /&gt;
Anschaulich bedeutet das:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Ein NP-schweres Problem ist mindestens so schwierig wie die schwierigsten Probleme aus NP.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wichtig ist: Ein NP-schweres Problem muss selbst nicht in NP liegen. Es kann also auch ein Optimierungsproblem sein und muss nicht zwingend ein Entscheidungsproblem sein.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Das Optimierungsproblem des Traveling Salesman Problems lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Finde die kürzeste Rundreise durch alle Städte.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses Optimierungsproblem ist NP-schwer.&lt;br /&gt;
&lt;br /&gt;
== NP-vollständig ==&lt;br /&gt;
&lt;br /&gt;
Ein Problem ist &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;, wenn zwei Bedingungen erfüllt sind:&lt;br /&gt;
&lt;br /&gt;
# Das Problem liegt in NP.&lt;br /&gt;
# Das Problem ist NP-schwer.&lt;br /&gt;
&lt;br /&gt;
Formal:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
\text{NP-vollständig} = \text{NP} \cap \text{NP-schwer}&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NP-vollständige Probleme sind also die schwierigsten Probleme innerhalb der Klasse NP.&lt;br /&gt;
&lt;br /&gt;
=== Beispiel ===&lt;br /&gt;
&lt;br /&gt;
Das Entscheidungsproblem des Traveling Salesman Problems:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gibt es eine Rundreise mit einer Länge von höchstens K?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ist NP-vollständig.&lt;br /&gt;
&lt;br /&gt;
== Unterschied zwischen NP-schwer und NP-vollständig ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Begriff&lt;br /&gt;
! Bedeutung&lt;br /&gt;
! Muss in NP liegen?&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP&#039;&#039;&#039;&lt;br /&gt;
| Lösungen sind schnell überprüfbar&lt;br /&gt;
| Ja&lt;br /&gt;
| TSP als Entscheidungsproblem&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;&lt;br /&gt;
| Mindestens so schwer wie alle Probleme in NP&lt;br /&gt;
| Nein&lt;br /&gt;
| TSP als Optimierungsproblem&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;&lt;br /&gt;
| In NP und zugleich NP-schwer&lt;br /&gt;
| Ja&lt;br /&gt;
| TSP als Entscheidungsproblem&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== P vs. NP ==&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;P-vs.-NP-Problem&#039;&#039;&#039; ist eines der bekanntesten offenen Probleme der Informatik.&lt;br /&gt;
&lt;br /&gt;
Die zentrale Frage lautet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Gilt P = NP?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder anders formuliert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Kann jedes Problem, dessen Lösung schnell überprüft werden kann, auch schnell gelöst werden?&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Bedeutung von P = NP ==&lt;br /&gt;
&lt;br /&gt;
Falls &#039;&#039;&#039;P = NP&#039;&#039;&#039; gelten würde, dann könnten alle Probleme in NP effizient gelöst werden. Das hätte weitreichende Folgen, zum Beispiel für:&lt;br /&gt;
&lt;br /&gt;
* Optimierung&lt;br /&gt;
* Kryptographie&lt;br /&gt;
* Logistik&lt;br /&gt;
* Planung&lt;br /&gt;
* Künstliche Intelligenz&lt;br /&gt;
&lt;br /&gt;
Viele heute als schwer geltende Probleme könnten dann effizient gelöst werden.&lt;br /&gt;
&lt;br /&gt;
== Bedeutung von P ≠ NP ==&lt;br /&gt;
&lt;br /&gt;
Die meisten Fachleute vermuten, dass gilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;math&amp;gt;&lt;br /&gt;
P \neq NP&lt;br /&gt;
&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das würde bedeuten:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Es gibt Probleme, deren Lösungen schnell überprüft, aber nicht schnell gefunden werden können.&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies erklärt, warum für viele schwierige Optimierungsprobleme keine effizienten exakten Algorithmen bekannt sind.&lt;br /&gt;
&lt;br /&gt;
== Grafische Einordnung ==&lt;br /&gt;
&lt;br /&gt;
Falls &amp;lt;math&amp;gt;P \neq NP&amp;lt;/math&amp;gt; gilt, ergibt sich folgende typische Einordnung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Probleme&lt;br /&gt;
├── P&lt;br /&gt;
│   └── effizient lösbare Probleme&lt;br /&gt;
│&lt;br /&gt;
├── NP&lt;br /&gt;
│   ├── P&lt;br /&gt;
│   └── NP-vollständige Probleme&lt;br /&gt;
│&lt;br /&gt;
└── NP-schwere Probleme&lt;br /&gt;
    ├── NP-vollständige Probleme&lt;br /&gt;
    └── weitere, nicht zwingend in NP liegende Probleme&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relevanz für Optimierungsprobleme ==&lt;br /&gt;
&lt;br /&gt;
Viele praktische Optimierungsprobleme sind NP-schwer. Dazu gehören beispielsweise:&lt;br /&gt;
&lt;br /&gt;
* Routenplanung&lt;br /&gt;
* Produktionsplanung&lt;br /&gt;
* Scheduling&lt;br /&gt;
* Rucksackproblem&lt;br /&gt;
* Zuordnungsprobleme&lt;br /&gt;
* viele gemischt-ganzzahlige Optimierungsprobleme&lt;br /&gt;
&lt;br /&gt;
Da exakte Verfahren bei großen Instanzen oft zu langsam sind, werden in der Praxis häufig approximative Verfahren eingesetzt, zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* Heuristiken&lt;br /&gt;
* Metaheuristiken&lt;br /&gt;
* Greedy-Verfahren&lt;br /&gt;
* Branch-and-Bound mit Abbruchkriterien&lt;br /&gt;
* evolutionäre Algorithmen&lt;br /&gt;
&lt;br /&gt;
== Merksätze ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;P&#039;&#039;&#039;: Probleme sind schnell lösbar.&lt;br /&gt;
* &#039;&#039;&#039;NP&#039;&#039;&#039;: Lösungen sind schnell überprüfbar.&lt;br /&gt;
* &#039;&#039;&#039;NP-schwer&#039;&#039;&#039;: Mindestens so schwer wie alle Probleme in NP.&lt;br /&gt;
* &#039;&#039;&#039;NP-vollständig&#039;&#039;&#039;: In NP und NP-schwer.&lt;br /&gt;
* &#039;&#039;&#039;P vs. NP&#039;&#039;&#039;: Offene Frage, ob schnelles Überprüfen auch schnelles Lösen bedeutet.&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
&lt;br /&gt;
* [[Optimierungsverfahren]]&lt;br /&gt;
* [[Heuristische Optimierungsverfahren]]&lt;br /&gt;
* [[Metaheuristische Verfahren]]&lt;br /&gt;
* [[Branch-and-Bound-Verfahren]]&lt;br /&gt;
* [[Multi-Objective Optimization]]&lt;/div&gt;</summary>
		<author><name>PhilKa</name></author>
	</entry>
</feed>