ASP.NET Core Blazor z programem Entity Framework Core (EF Core)
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
Ważne
Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
W tym artykule wyjaśniono, jak używać programu Entity Framework Core (EF Core) w aplikacjach po stronie Blazor serwera.
Po stronie Blazor serwera jest stanowa struktura aplikacji. Aplikacja utrzymuje bieżące połączenie z serwerem, a stan użytkownika jest przechowywany w pamięci serwera w obwodzie. Przykładem stanu użytkownika są dane przechowywane w wystąpieniach usługi wstrzykiwania zależności (DI), które są ograniczone do obwodu. Unikatowy model aplikacji, który udostępnia, Blazor wymaga specjalnego podejścia do korzystania z platformy Entity Framework Core.
Uwaga
Ten artykuł dotyczy EF Core aplikacji po stronie Blazor serwera. Blazor WebAssembly aplikacje są uruchamiane w piaskownicy zestawu WebAssembly, która uniemożliwia korzystanie z większości bezpośrednich połączeń z bazą danych. Uruchamianie EF Core w programie Blazor WebAssembly wykracza poza zakres tego artykułu.
Te wskazówki dotyczą składników, które przyjmują interakcyjne renderowanie po stronie serwera (interakcyjne SSR) w systemie Blazor Web App.
Te wskazówki dotyczą Server
projektu hostowanego Blazor WebAssembly rozwiązania lub Blazor Server aplikacji.
Bezpieczny przepływ uwierzytelniania wymagany dla aplikacji produkcyjnych
Ten artykuł dotyczy korzystania z lokalnej bazy danych, która nie wymaga uwierzytelniania użytkownika. Aplikacje produkcyjne powinny korzystać z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Aby uzyskać więcej informacji na temat uwierzytelniania dla wdrożonych aplikacji testowych i produkcyjnychBlazor, zobacz artykuły w temacie BlazorZabezpieczenia i Identity węzeł.
W przypadku usług platformy Microsoft Azure zalecamy używanie tożsamości zarządzanych. Tożsamości zarządzane bezpiecznie uwierzytelniają się w usługach platformy Azure bez przechowywania poświadczeń w kodzie aplikacji. Aby uzyskać więcej informacji, zobacz następujące zasoby:
- Co to są tożsamości zarządzane dla zasobów platformy Azure? (Dokumentacja firmy Microsoft Entra)
- Dokumentacja usług platformy Azure
Samouczek dotyczący tworzenia Blazor aplikacji bazy danych filmów
Aby zapoznać się z samouczkiem dotyczącym tworzenia aplikacji korzystającej z EF Core na potrzeby operacji bazy danych, zobacz Build a Blazor movie database app (Overview). W samouczku pokazano, jak utworzyć element, który Blazor Web App może wyświetlać filmy w bazie danych filmów i zarządzać nimi.
Dostęp do bazy danych
EF Core opiera się na metodzie DbContextkonfigurowania dostępu do bazy danych i działania jako jednostki pracy. EF Core AddDbContext Udostępnia rozszerzenie dla aplikacji ASP.NET Core, które rejestrują kontekst jako usługę o określonym zakresie. W aplikacjach po stronie Blazor serwera rejestracje usług w zakresie mogą być problematyczne, ponieważ wystąpienie jest współużytkowane między składnikami w obwodzie użytkownika. DbContext nie jest bezpieczny wątkiem i nie jest przeznaczony do współbieżnego użycia. Istniejące okresy istnienia są nieodpowiednie z następujących powodów:
- Stan pojedynczego udziału dla wszystkich użytkowników aplikacji i prowadzi do niewłaściwego współbieżnego użycia.
- Zakres (wartość domyślna) stanowi podobny problem między składnikami dla tego samego użytkownika.
- Przejściowe wyniki w nowym wystąpieniu na żądanie, ale ponieważ składniki mogą być długotrwałe, powoduje to dłuższy kontekst niż może być zamierzony.
Poniższe zalecenia zostały zaprojektowane w celu zapewnienia spójnego podejścia do korzystania z EF Core aplikacji po stronie Blazor serwera.
Rozważ użycie jednego kontekstu na operację. Kontekst jest przeznaczony do szybkiego tworzenia wystąpienia niskiego obciążenia:
using var context = new ProductsDatabaseContext(); return await context.Products.ToListAsync();
Użyj flagi, aby zapobiec wielu równoczesnym operacjom:
if (Loading) { return; } try { Loading = true; ... } finally { Loading = false; }
Umieść operacje bazy danych po wierszu
Loading = true;
w blokutry
.Bezpieczeństwo wątków nie jest problemem, więc logika ładowania nie wymaga blokowania rekordów bazy danych. Logika ładowania służy do wyłączania kontrolek interfejsu użytkownika, dzięki czemu użytkownicy nie mogą przypadkowo wybierać przycisków ani aktualizować pól podczas pobierania danych.
Jeśli istnieje prawdopodobieństwo, że wiele wątków może uzyskać dostęp do tego samego bloku kodu, wstrzyknąć fabrykę i utworzyć nowe wystąpienie na operację. W przeciwnym razie wstrzykiwanie i używanie kontekstu jest zwykle wystarczające.
W przypadku długotrwałych operacji, które korzystają ze EF Coreśledzenia zmian lub kontroli współbieżności, określ zakres kontekstu na okres istnienia składnika.
Nowe DbContext
wystąpienia
Najszybszym sposobem utworzenia nowego DbContext wystąpienia jest utworzenie new
nowego wystąpienia. Istnieją jednak scenariusze wymagające rozwiązania dodatkowych zależności:
- Użyj polecenia
DbContextOptions
, aby skonfigurować kontekst. - Użycie parametry połączenia na DbContext, na przykład w przypadku korzystania z Identity ASP.NET Core. Aby uzyskać więcej informacji, zobacz Multi-tenancy (EF Core dokumentacja).
Ostrzeżenie
Nie przechowuj wpisów tajnych aplikacji, parametry połączenia, poświadczeń, haseł, osobistych numerów identyfikacyjnych (PIN), prywatnego kodu C#/.NET lub kluczy prywatnych/tokenów w kodzie po stronie klienta, który jest zawsze niepewny. W środowiskach testowych/przejściowych i produkcyjnych kod po stronie Blazor serwera i internetowe interfejsy API powinny używać bezpiecznych przepływów uwierzytelniania, które unikają utrzymywania poświadczeń w kodzie projektu lub plikach konfiguracji. Poza lokalnymi testami programistycznymi zalecamy unikanie używania zmiennych środowiskowych do przechowywania poufnych danych, ponieważ zmienne środowiskowe nie są najbezpieczniejszym podejściem. W przypadku lokalnego testowania programistycznego narzędzie Secret Manager jest zalecane do zabezpieczania poufnych danych. Aby uzyskać więcej informacji, zobacz Bezpieczne utrzymywanie poufnych danych i poświadczeń.
Zalecanym podejściem do utworzenia nowego DbContext elementu z zależnościami jest użycie fabryki. EF Core Wersja 5.0 lub nowsza udostępnia wbudowaną fabrykę do tworzenia nowych kontekstów.
W wersjach platformy .NET wcześniejszych niż 5.0 użyj następującego 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);
}
}
W poprzedniej fabryce:
- ActivatorUtilities.CreateInstance spełnia wszelkie zależności za pośrednictwem dostawcy usług.
- IDbContextFactory<TContext> jest dostępna w wersji EF Core ASP.NET Core 5.0 lub nowszej, dlatego powyższy interfejs jest wymagany tylko dla ASP.NET Core 3.x.
Poniższy przykład umożliwia skonfigurowanie SQLite i włączenie rejestrowania danych w aplikacji, która zarządza kontaktami. Kod używa metody rozszerzenia (AddDbContextFactory), aby skonfigurować fabrykę baz danych dla di i udostępnić opcje domyślne:
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"));
Fabryka wstrzykuje komponenty, aby utworzyć nowe wystąpienia DbContext.
Nadawanie zakresu kontekstowi bazy danych w odniesieniu do metody składnika
Fabryka jest używana do wstrzykiwania do komponentu.
@inject IDbContextFactory<ContactContext> DbFactory
Utwórz DbContext dla metody przy użyciu fabryki (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();
}
}
}
Określanie zakresu kontekstu bazy danych w odniesieniu do cyklu życia składnika
Możesz utworzyć element DbContext , który istnieje przez okres istnienia składnika. Dzięki temu można używać go jako jednostki pracy i korzystać z wbudowanych funkcji, takich jak śledzenie zmian i rozpoznawanie współbieżności.
Zaimplementuj IDisposable i wstrzyknij fabrykę do składnika.
@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
Ustanów właściwość dla DbContext:
private ContactContext? Context { get; set; }
OnInitializedAsync
jest zastępowana w celu utworzenia DbContext:
protected override async Task OnInitializedAsync()
{
Context = DbFactory.CreateDbContext();
}
DbContext jest usuwany wraz z usunięciem składnika.
public void Dispose() => Context?.Dispose();
Włączanie rejestrowania poufnych danych
EnableSensitiveDataLogging zawiera dane aplikacji w komunikatach o wyjątkach i rejestrowaniu struktury. Zarejestrowane dane mogą zawierać wartości przypisane do właściwości wystąpień jednostki i wartości parametrów dla poleceń wysyłanych do bazy danych. Rejestrowanie danych EnableSensitiveDataLogging za pomocą elementu jest zagrożeniem bezpieczeństwa, ponieważ może uwidaczniać hasła i inne dane osobowe podczas rejestrowania instrukcji SQL wykonywanych względem bazy danych.
Zalecamy włączenie EnableSensitiveDataLogging tylko na potrzeby programowania i testowania:
#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