16. Jun 2025
Lesedauer 11 Min.
Datenzugriff in .NET-Apps
EF Core in WinForms, WPF, WinUI 3, Blazor und .NET MAUI
Datenbankanbindung in .NET – vom Desktop bis zur mobilen App.

Der Zugriff auf Daten spielt in nahezu allen modernen Anwendungen eine zentrale Rolle – sei es zur Anzeige von Benutzerinformationen, zur Verarbeitung von Eingaben oder zur Synchronisation mit Cloud-Diensten. Mit dem .NET-Ökosystem steht ein vielseitiges Framework für den Datenzugriff zur Verfügung. Besonders hervorzuheben ist Entity Framework Core (EF Core), das sich als leistungsfähiger, flexibler und plattformübergreifender objektrelationaler Mapper (ORM) etabliert hat. Trotz der gemeinsamen technischen Basis von EF Core unterscheiden sich die Anforderungen und Umsetzungen zum Teil erheblich: Eine WinForms- oder WPF-Anwendung hat andere Anforderungen an UI-Datenbindung und Lebenszyklusmanagement als eine Blazor-Web-App oder eine mobile App mit .NET MAUI. Auch die Art des Datenzugriffs – lokal, über ein API oder direkt auf eine entfernte Datenbank – variiert je nach Plattform und Architektur.
Grundlagen der Datenbanknutzung in .NET
Bevor man sich mit den Besonderheiten einzelner .NET-App-Typen beschäftigt, ist es wichtig, die gemeinsamen Grundlagen des Datenbankzugriffs zu verstehen. Das .NET-Ökosystem bietet mehrere Möglichkeiten, um mit relationalen Datenbanken zu arbeiten – darunter ADO.NET und EF Core Dieser Artikel beleuchtet EF Core, da es moderne Entwicklungsansätze unterstützt, eine hohe Integration in .NET-Projekte bietet und sich plattformübergreifend einsetzen lässt – siehe Kasten ADO.NET versus EF Core.ADO.NET versus EF Core
In der .NET-Welt gibt es zwei Ansätze für den Zugriff auf relationale Datenbanken: den Weg mit ADO.NET und den objektorientierten Ansatz über EF Core. Beide Methoden führen zum Ziel: dem Lesen, Schreiben und Verwalten von Daten. Sie unterscheiden sich grundlegend in ihrer Herangehensweise, im Komfort und im Grad der Abstraktion.
Entity Framework Core im Überblick
EF Core abstrahiert viele Details des Datenbankzugriffs. Es unterstützt Datenbankanbieter wie SQL Server, SQLite, PostgreSQL, MySQL und In-Memory-Datenbanken. Herzstück von EF Core ist die zentrale Klasse DbContext, die für die Verbindung zur Datenbank, das Laden von Daten und die Nachverfolgung von Änderungen an Entitäten verantwortlich ist. Die Tabellen der Datenbank werden in EF Core durch C#-Klassen (Entitäten) abgebildet, mit denen direkt gearbeitet werden kann [1]. EF Core unterstützt zwei Ansätze, um ein Datenmodell in einer Anwendung zu integrieren: Code First und Database First:- Code First – Modellgestützte Entwicklung: Bei diesem Ansatz wird das Datenmodell vollständig im Code entworfen – durch die Definition von C#-Klassen, welche die Tabellen und Relationen der Datenbank beschreiben. EF Core kann dann auf Basis dieses Modells eine Datenbank erstellen. Änderungen an den Klassen werden über Migrationen verfolgt und auf die Datenbank übertragen. Dieser Ansatz ist dann sinnvoll, wenn man eine neue Anwendung von Grund auf entwickelt oder die volle Kontrolle über das Datenmodell haben möchte.
- Database First – Arbeiten mit einer bestehenden Datenbank: Hier ist die Datenbank bereits vorhanden, und EF Core generiert daraus die passenden C#-Entitäten. Dies erfolgt über das Kommando Scaffold-DbContext, das anhand des Schemas alle benötigten Klassen, DbSets und Konfigurationen erzeugt. Dieser Weg eignet sich, wenn man mit einer bestehenden oder fremdverwalteten Datenbank arbeitet – zum Beispiel bei der Integration in ein Altsystem. Der Nachteil: Änderungen am Modell im Code müssen zurück in die Datenbank gespiegelt werden.
- Änderungen im Code vornehmen (zum Beispiel eine neue Eigenschaft in einer Entitätsklasse).
- Eine neue Migration generieren mit dotnet ef migrations add Telefonnummer.
- Die Migration anwenden mit dotnet ef database update.
Konsolen-Applikation
Starten wir mit einer Konsolen-App, gewissermaßen als universellem Einstiegspunkt für alle nachfolgend beschriebenen .NET-basierten App-Typen. Das Beispiel modelliert eine einfache To-do-App (Aufgaben speichern, anzeigen, aktualisieren, löschen). Wir verwenden EF Core mit dem Code-First-Ansatz und SQLite.Schritt 1: Neues Projekt anlegen. Erstellen Sie ein neues Projekt des Typs .NET-Konsolenanwendung.Schritt 2: NuGet-Pakete installieren. Fügen Sie folgende Pakete hinzu: Microsoft.EntityFrameworkCore.Sqlite und Microsoft.EntityFrameworkCore.Tools.Schritt 3: Modellklassen erstellen. Erstellen Sie die Klassen User.cs und TaskItem.cs.
// User.cs
public <span class="hljs-keyword">class</span> <span class="hljs-title">User</span>
{
public int <span class="hljs-keyword">UserId</span> { get; set; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-keyword">Name</span> { get; set; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">List</span><TaskItem> <span class="hljs-keyword">Tasks</span> { get; set; } = new();
}
// TaskItem.cs
public <span class="hljs-keyword">class</span> <span class="hljs-title">TaskItem</span>
{
public int <span class="hljs-keyword">TaskItemId</span> { get; set; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-keyword">Title</span> { get; set; }
...
}
Schritt 4: DbContext erstellen:
// AppDbContext.cs
using Microsoft.EntityFrameworkCore;
public <span class="hljs-keyword">class</span> <span class="hljs-title">AppDbContext</span> : <span class="hljs-title">DbContext</span>
{
public DbSet<User> <span class="hljs-keyword">Users</span> { get; set; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">DbSet</span><TaskItem> <span class="hljs-keyword">Tasks</span> { get; set; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-keyword">DbPath</span> { get; }
<span class="hljs-keyword">public</span> <span class="hljs-keyword">AppDbContext</span>()
{
var folder =
Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, <span class="hljs-string">"tasks.db"</span>);
}
protected override void OnConfiguring(
DbContextOptionsBuilder options)
=> options.UseSqlite($<span class="hljs-string">"Data Source={DbPath}"</span>);
}
Schritt 5: Migration und Datenbank erstellen. Öffnen Sie die Paketmanager-Konsole und führen Sie folgende Befehle aus: Add-Migration InitialCreate und Update-Database.EF Core erstellt nun den Ordner Migrations und legt dort die generierten SQL-Statements für die Datenbankstruktur ab (Bild 1). Update-Database erzeugt anschließend die SQLite-Datenbankdatei tasks.db im lokalen AppData-Ordner des aktuellen Benutzers.

Von EF Core generierte DB-Migration (Bild 1)
Autor
Schritt 6: CRUD-Operationen in Program.cs. Jetzt können Sie die Datenbank direkt verwenden:
using var db = new AppDbContext();
// Create
var user = new User { Name = "Anna " };
user.Tasks.Add(new TaskItem
{
Title = "Einkaufen",
IsCompleted = false });
user.Tasks.Add(new TaskItem
{
Title = "E-Mail beantworten",
IsCompleted = true });
db.Users.Add(user);
db.SaveChanges();
// Read
var loadedUser = db.Users
.Where(u => u.Name == "Anna")
.Include(u => u.Tasks)
.FirstOrDefault();
if (loadedUser is not null)
{
Console.WriteLine(
$"Aufgaben für {loadedUser.Name}:");
foreach (var task in loadedUser.Tasks)
Console.WriteLine($"- {task.Title} (Erledigt:
{task.IsCompleted})");
}
Mit wenigen Klassen und den EF-Core-Tools kann in kürzester Zeit eine DB-Anwendung entstehen. Die Schritte bilden eine Grundlage, um auch in komplexeren Projekten (WinForms, WPF, Blazor, MAUI) auf dieselben Mechanismen zurückzugreifen.
Desktop-Apps mit WinForms
Viele Geschäftsanwendungen basieren auf dieser Plattform. Große Vorteile von WinForms sind das schnelle Erstellen von Benutzeroberflächen und die direkte, ereignisgesteuerte Programmierung. In Verbindung mit EF Core lässt sich auch in WinForms-Anwendungen ein objektorientierter Datenzugriff umsetzen.In aktuelle WinForms-Projekte lässt sich EF Core problemlos integrieren. Voraussetzung ist die Installation des passenden NuGet-Pakets; für SQLite ist dies zum Beispiel das Paket Microsoft.EntityFrameworkCore.Sqlite. Ebenso notwendig ist die Klasse DbContext, wie im vorherigen Abschnitt beschrieben. In WinForms-Apps wird DbContext meist direkt in der jeweiligen Formular-Klasse im Konstruktor oder in einem Lade-Event instanziert:
public partial class MainForm : Form
{
private readonly AppDbContext _db;
public MainForm()
{
InitializeComponent();
_db = new AppDbContext();
// alternativ mit Konfigurationsoptionen
}
}
Für produktive Anwendungen empfiehlt es sich, die Instanzierung auszulagern und beispielsweise über eine zentrale Factory oder einen Service zu kapseln. Es ist empfehlenswert, für jede Aktion eine neue DBContext-Instanz zu verwenden. Hier ein Beispiel für eine einfache, kurzlebige Nutzung:
private void LadeKunden()
{
using var db = new AppDbContext();
var kunden = db.Kunden.ToList();
kundenListBox.DataSource = kunden;
kundenListBox.DisplayMember = "Name";
}
WinForms bietet eine einfache Datenbindung über das DataSource-Attribut vieler Steuerelemente wie ListBox oder DataGridView. Bei EF-Core-Objekten wird keine automatische Änderungsverfolgung im UI durchgeführt. Änderungen müssen explizit zurück in den Kontext geschrieben werden. Es gibt einige Besonderheiten zu berücksichtigen:
- Fehlende Trennung von Logik und UI: WinForms verleitet dazu, Datenzugriffe direkt im UI-Code zu implementieren. Für bessere Wartbarkeit sollte man die Logik in separate Klassen oder Services auslagern.
- Keine native Dependency Injection (DI): Moderne Patterns wie DI oder MVVM sind in WinForms nicht vorgesehen, können aber mit Zusatzaufwand eingebunden werden.
- Threading-Probleme: Da WinForms keine asynchronen Operationen verwendet, muss auf Invoke/BeginInvoke geachtet werden, um UI-Updates im richtigen Thread auszuführen.
WPF (Windows Presentation Foundation)
WPF ist eine leistungsfähige UI-Technologie im .NET-Umfeld, die sich durch ihre gute Trennung von Logik und Oberfläche, Datenbindung sowie ihre Unterstützung des MVVM-Patterns (Model-View-ViewModel) auszeichnet. Diese Architektur harmoniert mit EF Core. In einer typischen WPF-Anwendung wird der Datenzugriff über Services realisiert, die vom ViewModelaus verwendet werden. Das ViewModel kommuniziert nicht direkt mit dem DbContext, sondern über eine abstrahierte Schicht, die den Datenzugriff kapselt. Ein beispielhafter Aufbau:- Model: Enthält die EF-Core-Entitäten (zum Beispiel Kunde, Bestellung)
- ViewModel: Ruft Daten über ein Repository ab und stellt ObservableCollections bereit.
- View: Bindet per XAML an ViewModel-Eigenschaften.
public partial class App : Application
{
public static IServiceProvider Services {
get; private set; }
protected override void OnStartup(
StartupEventArgs e)
{
var services = new ServiceCollection();
services.AddDbContext<AppDbContext>(options =>
options.UseSqlite("Data Source=app.db"));
services.AddTransient<KundeViewModel>();
Services = services.BuildServiceProvider();
var mainWindow = new MainWindow
{
DataContext = Services.
GetRequiredService<KundeViewModel>()
};
mainWindow.Show();
}
}
Ein zentrales Element des MVVM-Patterns in WPF ist das ViewModel. In Verbindung mit EF Core sollte der Datenzugriff asynchron erfolgen, um das UI nicht zu blockieren:
public class KundeViewModel : INotifyPropertyChanged
{
private readonly AppDbContext _dbContext;
public ObservableCollection<Kunde> Kunden {
get; } = new();
public KundeViewModel(AppDbContext dbContext)
{
_dbContext = dbContext;
_ = LadeKundenAsync();
}
private async Task LadeKundenAsync()
{
var daten = await _dbContext.Kunden.ToListAsync();
Kunden.Clear();
foreach (var k in daten)
Kunden.Add(k);
}
// INotifyPropertyChanged Implementierung ...
}
So wird sichergestellt, dass das UI reaktionsfähig bleibt. WPF erlaubt eine sehr komfortable Datenbindung per XAML, zum Beispiel an ein DataGrid:
<DataGrid ItemsSource="{Binding Kunden}"
AutoGenerateColumns="True"/>
Wird das ViewModel korrekt gesetzt und die Liste Kunden als ObservableCollection bereitgestellt, aktualisiert sich das UI automatisch bei Änderungen. WPF in Kombination mit EF Core bietet ein solides Fundament für datenbankgestützte Desktop-Anwendungen.
WinUI 3
Für aktuelle Windows-Apps ist WinUI 3 der empfohlene Weg. In Kombination mit Entity Framework Core lassen sich robuste, datengetriebene Anwendungen realisieren, mit einigen Besonderheiten in Bezug auf Projektstruktur, App-Lebenszyklus und Packaging. WinUI-3-Apps basieren auf einer modernen Architektur. Das UI wird in XAML definiert und die Logik in C# implementiert. WinUI 3 ist für asynchrone Datenverarbeitung, DI und das Pattern MVVM ausgelegt. EF Core lässt sich auch in WinUI 3 verwenden. Voraussetzung ist die Installation der EF-Core-Pakete über NuGet, hier das Paket Microsoft.EntityFrameworkCore.Sqlite:
public partial class App : Application
{
public static IServiceProvider Services {
get; private set; }
public App()
{
var services = new ServiceCollection();
services.AddDbContext<AppDbContext>(
options => options.UseSqlite(
"Data Source=app.db"));
services.AddSingleton<MainViewModel>();
Services = services.BuildServiceProvider();
this.InitializeComponent();
}
protected override void OnLaunched(
LaunchActivatedEventArgs args)
{
var mainWindow = new MainWindow
{
DataContext = Services.GetRequiredService<
MainViewModel>()
};
mainWindow.Activate();
}
}
Wie bei WPF bietet sich in WinUI 3 die Verwendung von MVVM an – viele nutzen Bibliotheken wie CommunityToolkit.Mvvm, um sich wiederholende Strukturen zu erleichtern. Hier ein Beispiel eines ViewModels:
public partial class MainViewModel : ObservableObject
{
private readonly AppDbContext _dbContext;
[ObservableProperty]
private ObservableCollection<Kunde> kunden;
public MainViewModel(AppDbContext dbContext)
{
_dbContext = dbContext;
_ = LadeKundenAsync();
}
private async Task LadeKundenAsync()
{
var daten =
await _dbContext.Kunden.ToListAsync();
Kunden = new ObservableCollection<
Kunde>(daten);
}
}
Die ObservableProperty-Attribute stammen aus dem Community Toolkit und vereinfachen PropertyChanged-Benachrichtigungen. WinUI 3 verwendet XAML, ähnlich wie WPF. Hier ein Beispiel für eine ListView:
<ListView ItemsSource="{Binding Kunden}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:Kunde">
<TextBlock Text="{x:Bind Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Durch Verwendung von x:Bind (statt Binding) können Typinformationen zur Kompilierzeit geprüft werden – das verbessert Performance und Fehlersicherheit.WinUI-3-Apps werden in der Regel als MSIX-Pakete ausgeliefert. Das bringt einige Einschränkungen und Besonderheiten mit sich:
- Dateipfade: Schreibrechte sind auf bestimmte Verzeichnisse beschränkt (zum Beispiel auf den ApplicationData.Current.LocalFolder) – das ist relevant bei SQLite-Dateien.
- Verbindungszeichenfolgen: Bei SQLite muss der Pfad zur Datenbank dynamisch zur Laufzeit ermittelt werden.
- App-Initialisierung:OnLaunched ersetzt Main() – hier wird typischerweise die Hauptseite geöffnet.
Blazor-Apps
Blazor ist Microsofts Webtechnologie auf Basis von .NET, die es ermöglicht, interaktive Webanwendungen mit C# zu entwickeln. Es gibt mehrere Render-Modi (statischer Server, Server interaktiv, WebAssembly, Auto). Bei serverseitigen Web-Apps läuft die Applikation auf dem Server und kommuniziert über SignalR mit dem Browser. Bei einer clientseitigen Ausführung wird die gesamte App in den Browser geladen und dort ausgeführt. In serverseitigen Apps lässt sich EF Core direkt verwenden. In Blazor WebAssembly ist ein direkter Datenbankzugriff aus Sicherheits- und Architekturgründen nicht möglich. Hier erfolgt der Zugriff über eine API-Schicht.Die Integration von EF Core in Blazor Server basiert auf einer Architektur mit den folgenden Bausteinen [2]:- DbContext und Datenmodelle: DbContext dient als Verbindung zur Datenbank und enthält die Definition von Tabellen (DbSet) und die Konfiguration von Beziehungen. Datenmodelle definieren die Struktur der Tabellen und ermöglichen die Abbildung von Datenbankeinträgen auf C#-Objekte.
- Blazor-Komponenten: CRUD-Operationen werden in Blazor-Komponenten implementiert, welche Daten direkt über DbContext abrufen, bearbeiten und speichern. Das UI interagiert mit den Datenmodellen, um dynamische Inhalte bereitzustellen.
- DI: DbContext wird als Scoped Service registriert und steht somit allen Blazor-Komponenten zur Verfügung.

Beispiel-App (Blazor) mit EF Core (Bild 2)
Autor
public class Contact
{
public int Id { get; set; }
[Required]
[StringLength(100, ErrorMessage =
"First name cannot exceed 100 characters.")]
public string? FirstName { get; set; }
[StringLength(100, ErrorMessage =
"Last name cannot exceed 100 characters.")]
...
}
Die Klasse ContactContext definiert den DbContext mit einem Verweis auf die Datenklasse:
public sealed class ContactContext : DbContext
{
public DbSet<Contact>? Contacts { get; set; }
public ContactContext(
DbContextOptions<ContactContext> options) :
base(options)
{
// Konstruktorlogik...
}
protected override void OnModelCreating(
ModelBuilder modelBuilder)
{
// Modellkonfiguration...
}
}
Der DbContext muss in der Anwendung registriert werden. Dazu wird in der Datei Program.cs der ContactContext in den Dienstcontainer der Anwendung aufgenommen. Dies erfolgt durch die Methode AddDbContextFactory<ContactContext>. In diesem Fall wird eine SQLite-Datenbank verwendet:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContextFactory<
ContactContext>(opt => opt.UseSqlite(
$"Data Source = {nameof(
ContactContext.ContactsDb)}.db"));
// Weitere Konfigurationen...
var app = builder.Build();
// Datenbankinitialisierung...
app.Run();
In den Blazor-Komponenten wird der ContactContext über DI bereitgestellt. Dadurch können Datenbankoperationen direkt in den Komponenten durchgeführt werden. Beispielsweise wird in der Komponente AddContact.razor ein neues Contact-Objekt erstellt und zur Datenbank hinzugefügt:
@inject IDbContextFactory<ContactContext> DbFactory
@code {
private Contact? Contact { get; set; } = new();
private async Task ValidationResultAsync(
bool success)
{
if (success && Contact is not null)
{
using var context =
DbFactory.CreateDbContext();
context.Contacts?.Add(Contact);
await context.SaveChangesAsync();
// Weitere Logik...
}
}
}
Um die Lebensdauer der Datenbankverbindung zu steuern und potenzielle Konflikte zu vermeiden, wird der ContactContext als Scoped-Service registriert. Dies stellt sicher, dass für jede Benutzeranfrage in der Datei Program.cs eine neue Instanz von DbContext erstellt wird:
builder.Services.AddDbContextFactory<
ContactContext>(opt => opt.UseSqlite(
$"Data Source = {nameof(
ContactContext.ContactsDb)}.db"));
Betrachten wir beispielhaft eine Create-Operation. Sie ermöglicht das Hinzufügen neuer Datensätze zur Datenbank. In der Komponente AddContact.razor wird ein neues Contact-Objekt erstellt und zur Datenbank hinzugefügt:
@inject IDbContextFactory<ContactContext> DbFactory
@code {
private Contact? Contact { get; set; } = new();
private async Task ValidationResultAsync(
bool success)
{
if (success && Contact is not null)
{
using var context =
DbFactory.CreateDbContext();
context.Contacts?.Add(Contact);
await context.SaveChangesAsync();
// Weitere Logik...
}
}
}
.NET MAUI
.NET MAUI ermöglicht die Entwicklung von Apps für Android, iOS, Windows und macOS mit einer einzigen Codebasis. Für die Benutzeroberfläche werden XAML oder C# verwendet, während Geschäftslogik und Datenzugriff in gemeinsamem .NET-Code geschrieben werden. Anders als bei Desktop- oder Webanwendungen ist der direkte Zugriff auf relationale Datenbanken nicht praktikabel. Stattdessen ist SQLite die Standardlösung für lokale Persistenz. Bei verteilten Anwendungen wird über Web-APIs mit einem Backend kommuniziert, das EF Core nutzt. Man unterscheidet zwei Ansätze für Datenstrategien:- Lokale Datenbank mit SQLite und EF Core: Diese Kombination ist ideal für offlinefähige Apps, das Speichern von persönlichen Daten und für die temporäre Zwischenspeicherung.
- Servergestützte Datenhaltung via Web-API: Dieses Szenario eignet sich für eine zentrale Datenhaltung und Synchronisierung, die Benutzerverwaltung in Multi-User-Apps und sicherheitssensitive Daten.
SQLite in .NET MAUI ohne EF Core
SQLite ist die empfohlene Lösung für die lokale Datenhaltung in MAUI-Apps. Für offlinefähige Datenbanken eignet sich das Paket sqlite-net-pcl. Grundsätzlich sind mehrere Schritte zu durchlaufen [3]. Benötigt wird ein Datenmodell, beispielsweise eine Klasse TodoItem:
using SQLite;
public class TodoItem
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Title { get; set; }
public bool IsDone { get; set; }
}
Mit Attributen wie [PrimaryKey] und [AutoIncrement] wird das Datenbankschema automatisch abgeleitet. Für die Arbeit mit der Datenbank erstellen wir eine Klasse TodoDatabase:
using SQLite;
public class TodoDatabase
{
private readonly SQLiteAsyncConnection _database;
public TodoDatabase(string dbPath)
{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<TodoItem>().Wait();
}
public Task<List<TodoItem>> GetItemsAsync() =>
_database.Table<TodoItem>().ToListAsync();
public Task<int> SaveItemAsync(TodoItem item) =>
item.Id == 0 ? _database.InsertAsync(item) :
_database.UpdateAsync(item);
public Task<int> DeleteItemAsync(TodoItem item) =>
_database.DeleteAsync(item);
}
TodoDatabase ist eine Datenbank-Hilfsklasse, welche die lokale Speicherung mit SQLite umsetzt. Sie kapselt alle CRUD-Operationen (Create, Read, Update, Delete) für eine Entität des Typs TodoItem. In der Datei MauiProgram.cs wird der Pfad zur Datenbank gesetzt:
builder.Services.AddSingleton<TodoDatabase>(
provider => {
var dbPath = Path.Combine(
FileSystem.AppDataDirectory, "todos.db3");
return new TodoDatabase(dbPath);
});
Der Einsatz kann dann beispielsweise in einer ViewModel-Datei erfolgen:
public class TodoViewModel
{
private readonly TodoDatabase _db;
public ObservableCollection<TodoItem> Items {
get; } = new();
public TodoViewModel(TodoDatabase db)
{
_db = db;
_ = LoadAsync();
}
private async Task LoadAsync()
{
var items = await _db.GetItemsAsync();
Items.Clear();
foreach (var item in items)
Items.Add(item);
}
public async Task AddItemAsync(string title)
{
var item = new TodoItem { Title = title,
IsDone = false };
await _db.SaveItemAsync(item);
await LoadAsync();
}
}
Der Konstruktor lädt beim Start die gespeicherten Aufgaben aus der Datenbank. Mit LoadAsync() holtman alle Aufgaben aus der Datenbank und aktualisiert die Liste. Mittels AddItemAsync() wird eine Aufgabe hinzugefügt und die Liste neu geladen. Dieses Vorgehen ohne EF Core bietet die folgenden Vorteile: kein Overhead, keine Migrationen notwendig, schnell und leichtgewichtig und gut geeignet für eine lokale Datenspeicherung.Sehen wir uns im nächsten Schritt die Lösung mit EF Core und SQLite als Datenbank an [4]. Die dafür erforderlichen NuGet-Pakete sind: Microsoft.EntityFrameworkCore.Sqlite und Microsoft.EntityFrameworkCore.Tools. Es wird ein Modell (zum Beispiel Order) definiert und eine passende DbContext-Klasse erstellt:
public class Order
{
public int Id { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public AppDbContext(DbContextOptions<
AppDbContext> options) : base(options)
{
}
}
Da MAUI-Apps unter anderem auf Android, iOS und Windows laufen, wird der Pfad zur SQLite-Datei dynamisch gesetzt, zum Beispiel in der Datei MauiProgram.cs:
string dbPath = Path.Combine(
FileSystem.AppDataDirectory, "app.db");
builder.Services.AddDbContext<AppDbContext>(
options => options.UseSqlite(
$"Filename={dbPath}"));
EF Core unterstützt auch in MAUI die Code-First-Migrationsstrategie:
dotnet ef migrations add InitialCreate
dotnet ef database update
Wichtig: Migrationen müssen vor der Laufzeit ausgeführt werden. Mithilfe von AppDbContext können nun wie gewohnt Datenoperationen durchgeführt werden, zum Beispiel:
var orders = await db.Orders.ToListAsync();
Diese Zugriffe können aus einem ViewModel heraus erfolgen.
Fazit und Ausblick
Datenbankzugriffe mit EF Core lassen sich in allen .NET-App-Typen konsistent und flexibel umsetzen – vom klassischen Desktop bis zur mobilen App. EF Core vereinfacht die Arbeit mit relationalen Daten durch objektorientierte Datenmodelle, LINQ-basierte Abfragen und integrierte Migrationsunterstützung. Die Unterschiede zwischen App-Typen liegen weniger im Datenzugriff selbst als in den Rahmenbedingungen wie Datenbindung, Architektur (etwa MVVM) und Deployment. Besonders in Blazor und MAUI kommt dem Zusammenspiel von EF Core, APIs und lokaler Persistenz eine zentrale Rolle zu. Wer Architekturtrennung, Testbarkeit und langfristige Wartbarkeit im Blick hat, findet in EF Core ein solides Fundament für moderne .NET-Anwendungen.Fussnoten
- Microsoft Learn, Erste Schritte mit EF Core, http://www.dotnetpro.de/SL2506-07CRUD1
- GitHub, dotnet Blazor Samples, http://www.dotnetpro.de/SL2506-07CRUD2
- Microsoft Learn, Lokale .NET-MAUI-Datenbanken, http://www.dotnetpro.de/SL2506-07CRUD3
- Telerik, CRUD Operations & EF Core Migrations, http://www.dotnetpro.de/SL2506-07CRUD4