Freigeben über


ASP.NET Core Blazor mit Entity Framework Core (EF Core)

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der .NET- und .NET Core-Supportrichtlinie. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

In diesem Artikel wird erläutert, wie Sie Entity Framework Core (EF Core) in serverseitigen Blazor-Apps verwenden.

Serverseitiges Blazor ist ein zustandsbehaftetes App-Framework. Die App unterhält eine kontinuierliche Verbindung mit dem Server, und der Zustand des Benutzers wird im Arbeitsspeicher des Servers in einer Verbindung gespeichert. Ein Beispiel für den Benutzerzustand sind Daten, die in Dienstinstanzen mit -Abhängigkeitsinjektion (DI) gespeichert sind, die auf die Verbindung beschränkt sind. Das eindeutige Anwendungsmodell, das Blazor bereitstellt, erfordert eine besondere Vorgehensweise bei der Verwendung von Entity Framework Core.

Hinweis

In diesem Artikel wird EF Core in serverseitigen Blazor-Apps behandelt. Blazor WebAssembly-Apps werden in einer WebAssembly-Sandbox ausgeführt, die die meisten direkten Datenbankverbindungen verhindert. Die Ausführung von EF Core in Blazor WebAssembly sprengt jedoch den Rahmen dieses Artikels.

Dieser Leitfaden gilt für Komponenten, die interaktives serverseitiges Rendering (interactive SSR) in einer Blazor Web App übernehmen.

Dieser Leitfaden gilt für das Server Projekt einer gehosteten Blazor WebAssembly Lösung oder einer Blazor Server App.

Sicherer Authentifizierungsflow für Produktions-Apps erforderlich

Dieser Artikel bezieht sich auf die Verwendung einer lokalen Datenbank, die keine Benutzerauthentifizierung erfordert. Produktions-Apps sollten den sichersten verfügbaren Ablauf für die Authentifizierung verwenden. Weitere Informationen zur Authentifizierung für bereitgestellte Blazor-Apps in Test- und Produktionsumgebungen finden Sie in den Artikeln im Knoten BlazorSicherheit und Identity.

Für Microsoft Azure-Dienste empfehlen wir die Verwendung von verwalteten Identitäten. Verwaltete Identitäten authentifizieren sich sicher bei Azure-Diensten, ohne dass Anmeldedaten im App-Code gespeichert werden müssen. Weitere Informationen finden Sie in den folgenden Ressourcen:

Erstellen eines Lernprogramms für eine Blazor Filmdatenbank-App

Für ein Tutorial zum Erstellen einer App, die EF Core für Datenbankvorgänge verwendet, siehe Erstellen einer Blazor-Filmdatenbank-App (Übersicht). Das Lernprogramm zeigt, wie Sie ein Blazor Web App Video erstellen, das Filme in einer Filmdatenbank anzeigen und verwalten kann.

Datenbankzugriff

EF Core stützt sich auf einen DbContext als Mittel zur Konfiguration des Datenbankzugriffs und um als Arbeitseinheit zu fungieren. EF Core stellt die AddDbContext-Erweiterung für ASP.NET Core-Apps bereit, die den Kontext als scoped-Dienst registriert. In serverseitigen Blazor-Apps können bereichsbezogene Dienstregistrierungen problematisch sein, da die Instanz innerhalb der Verbindung des Benutzers bzw. der Benutzerin von allen Komponenten gemeinsam genutzt wird. DbContext ist nicht threadsicher und nicht für gleichzeitige Verwendung vorgesehen. Die vorhandenen Lebensdauern sind aus den folgenden Gründen ungeeignet:

  • Singleton verwendet den Zustand für alle Benutzer der App und führt zu einer ungeeigneten gleichzeitigen Verwendung.
  • Scoped (Bereichsbezogen) (die Standardeinstellung) stellt ein ähnliches Problem zwischen Komponenten für denselben Benutzer dar.
  • Transient (Vorübergehend) führt zu einer neuen Instanz pro Anforderung. Da Komponenten jedoch langlebig sein können, führt dies zu einem längerlebigen Kontext als möglicherweise beabsichtigt.

Die folgenden Empfehlungen sind darauf ausgelegt, einen konsistenten Ansatz zur Verwendung von EF Core in serverseitigen Blazor-Apps zu schaffen.

  • Erwägen Sie die Verwendung eines Kontexts pro Vorgang. Der Kontext ist für schnelle Instanziierung mit geringem Mehraufwand konzipiert:

    using var context = new ProductsDatabaseContext();
    
    return await context.Products.ToListAsync();
    
  • Verwenden Sie ein Flag, um mehrere gleichzeitige Vorgänge zu verhindern:

    if (Loading)
    {
        return;
    }
    
    try
    {
        Loading = true;
    
        ...
    }
    finally
    {
        Loading = false;
    }
    

    Platzieren Sie Datenbankvorgänge nach der Zeile Loading = true; im try-Block.

    Da die Threadsicherheit keine Rolle spielt, ist für die Ladelogik kein Sperren von Datenbankeinträgen erforderlich. Die Ladelogik wird verwendet, um Benutzeroberflächensteuerelemente zu deaktivieren, sodass Benutzer nicht versehentlich Schaltflächen auswählen oder Felder aktualisieren, während Daten abgerufen werden.

  • Wenn es möglich ist, dass mehrere Threads auf denselben Codeblock zugreifen könnten, fügen Sie eine Factory ein, und erstellen Sie eine neue Instanz pro Vorgang. Andernfalls reicht das Einfügen und das Verwenden des Kontexts in der Regel aus.

  • Bei langlebigen Vorgängen, die die EF Core-Vorteile der Änderungsnachverfolgung oder Parallelitätssteuerung nutzen, passen Sie den Kontext an die Lebensdauer der Komponente an.

Neue DbContext-Instanzen

Die schnellste Möglichkeit zum Erstellen einer neuen DbContext-Instanz ist die Verwendung von new, um eine neue Instanz zu erstellen. Es gibt jedoch Szenarien, in denen möglicherweise zusätzliche Abhängigkeiten aufgelöst werden müssen:

Warnung

Speichern Sie keine geheimen App-Schlüssel, Verbindungszeichenfolge s, Anmeldeinformationen, Kennwörter, persönliche Identifikationsnummern (PINs), privaten C#/.NET-Code oder private Schlüssel/Token im clientseitigen Code, der immer unsicher ist. In Test-/Staging- und Produktionsumgebungen sollten serverseitiger Blazor Code und Web-APIs sichere Authentifizierungsflüsse verwenden, die die Authentifizierung von Anmeldeinformationen innerhalb von Projektcode- oder Konfigurationsdateien vermeiden. Außerhalb der lokalen Entwicklungstests wird empfohlen, die Verwendung von Umgebungsvariablen zum Speichern vertraulicher Daten zu vermeiden, da Umgebungsvariablen nicht der sicherste Ansatz sind. Für lokale Entwicklungstests wird das Tool "Geheimer Manager" zum Sichern vertraulicher Daten empfohlen. Weitere Informationen finden Sie unter "Sichere Verwaltung vertraulicher Daten und Anmeldeinformationen".

Der empfohlene Lösung zum Erstellen eines neuen DbContext mit Abhängigkeiten besteht in der Verwendung einer Factory. EF Core 5.0 oder höher bietet eine integrierte Factory zum Erstellen neuer Kontexte.

Verwenden Sie in .NET-Versionen vor 5.0 die folgende DbContextFactory:

using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorServerDbContextExample.Data
{
    public class DbContextFactory<TContext> 
        : IDbContextFactory<TContext> where TContext : DbContext
    {
        private readonly IServiceProvider provider;

        public DbContextFactory(IServiceProvider provider)
        {
            this.provider = provider ?? throw new ArgumentNullException(
                $"{nameof(provider)}: You must configure an instance of " +
                "IServiceProvider");
        }

        public TContext CreateDbContext() => 
            ActivatorUtilities.CreateInstance<TContext>(provider);
    }
}

In der vorhergehenden Factory gilt Folgendes:

Im folgenden Beispiel werden SQLite konfiguriert und die Datenprotokollierung in einer App aktiviert, die Kontakte verwaltet. Der Code verwendet eine Erweiterungsmethode (AddDbContextFactory) zum Konfigurieren der Datenbankfactory für DI und bietet Standardoptionen:

builder.Services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
services.AddDbContextFactory<ContactContext>(opt =>
    opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));

Die Factory wird in Komponenten eingefügt, um neue DbContext-Instanzen zu erstellen.

Einen Datenbankkontext auf eine Komponentenmethode einschränken

Die Factory wird in die Komponente eingefügt:

@inject IDbContextFactory<ContactContext> DbFactory

Erstellen einer DbContext für eine Methode mithilfe der Factory (DbFactory):

private async Task DeleteContactAsync()
{
    using var context = DbFactory.CreateDbContext();

    if (context.Contacts is not null)
    {
        var contact = await context.Contacts.FirstAsync(...);

        if (contact is not null)
        {
            context.Contacts?.Remove(contact);
            await context.SaveChangesAsync();
        }
    }
}

Festlegen des Bereichs eines Datenbankkontexts auf die Lebensdauer der Komponente

Möglicherweise möchten Sie einen DbContext erstellen, der für die Lebensdauer einer Komponente vorhanden ist. Dies ermöglicht Ihnen die Verwendung als Arbeitseinheit und nutzt integrierte Features, z. B. Änderungsnachverfolgung und Parallelitätsauflösung.

Implementieren von IDisposable und Integrieren der Factory in die Komponente:

@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory

Festlegen einer Eigenschaft für den DbContext:

private ContactContext? Context { get; set; }

OnInitializedAsync wird überschrieben, um die DbContext zu erstellen:

protected override async Task OnInitializedAsync()
{
    Context = DbFactory.CreateDbContext();
}

Die DbContext wird verworfen, wenn die Komponente verworfen wird:

public void Dispose() => Context?.Dispose();

Aktivieren der Protokollierung von vertraulicher Daten

EnableSensitiveDataLogging schließt Anwendungsdaten in Ausnahmemeldungen und Frameworkprotokolle ein. Die protokollierten Daten können die den Eigenschaften von Entitätsinstanzen zugewiesenen Werte enthalten sowie Parameterwerte für Befehle, die an die Datenbank gesendet werden. Das Protokollieren von Daten mit EnableSensitiveDataLogging stellt ein Sicherheitsrisiko dar, da damit möglicherweise Kennwörter und andere personenbezogene Informationen (PII) verfügbar gemacht werden, wenn SQL-Anweisungen für die Datenbank protokolliert werden.

Es wird empfohlen, EnableSensitiveDataLogging ausschließlich für Entwicklung und Tests zu aktivieren:

#if DEBUG
    services.AddDbContextFactory<ContactContext>(opt =>
        opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db")
        .EnableSensitiveDataLogging());
#else
    services.AddDbContextFactory<ContactContext>(opt =>
        opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
#endif

Zusätzliche Ressourcen