Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Notatka
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.
Ostrzeżenie
Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz Zasady pomocy technicznej dla platform .NET i .NET Core. Aby zapoznać się z aktualną wersją, zobacz artykuł w wersji .NET 10.
W tym artykule wyjaśniono, jak używać programu Entity Framework Core (EF Core) w aplikacjach Blazor serwerowych.
Blazor po stronie serwera jest stanowym frameworkiem aplikacji. Aplikacja utrzymuje bieżące połączenie z serwerem, a stan użytkownika jest przechowywany w pamięci serwera w obwodzie usługi SignalR. Przykładem stanu użytkownika są dane przechowywane w wystąpieniach usługi wstrzykiwania zależności (DI), które są ograniczone do circuit. Unikatowy model aplikacji, który Blazor udostępnia, wymaga specjalnego podejścia do korzystania z platformy Entity Framework Core.
Notatka
Ten artykuł dotyczy EF Core aplikacji po stronie serwera Blazor. Blazor WebAssembly aplikacje są uruchamiane w piaskownicy 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ą komponentów, które stosują interakcyjne renderowanie po stronie serwera (interakcyjne SSR) w Blazor Web App.
Te wskazówki dotyczą projektu hostowanego rozwiązania Blazor WebAssembly lub aplikacji Blazor Server.
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 testowych i produkcyjnych Blazor aplikacji, zobacz artykuły w Blazorwęźle Zabezpieczenia i Identity.
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 Microsoft Entra)
- Dokumentacja usług platformy Azure
Samouczek tworzenia Blazor aplikacji bazy 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ć Blazor Web Appskładnik, który może wyświetlać filmy w bazie danych filmów i zarządzać nimi.
Dostęp do bazy danych
EF Core opiera się na DbContext jako środku do konfigurowania dostępu do bazy danych oraz działania jako jednostka pracy. EF Core AddDbContext udostępnia rozszerzenie dla aplikacji ASP.NET Core, które rejestruje kontekst jako usługę o określonym zakresie. W aplikacjach po stronie Blazor serwera rejestracje usług o zakresie ograniczonym mogą być problematyczne, ponieważ instancja jest współużytkowana między składnikami w połączeniu użytkownika. DbContext nie jest bezpieczny wątkowo i nie jest przeznaczony do współbieżnego użycia. Istniejące okresy ważności są nieodpowiednie z następujących powodów:
- Singleton współdzieli stan między wszystkimi użytkownikami aplikacji i prowadzi do niewłaściwego współbieżnego użycia.
- Scoped (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 w aplikacjach Blazor po stronie serwera.
Rozważ użycie jednego kontekstu na operację. Kontekst jest przeznaczony do szybkiego tworzenia instancji przy niskim narzucie:
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 klikali przypadkowo przycisków ani nie aktualizowali 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, należy 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ą z zalet EF Coreśledzenia zmian lub kontroli współbieżności, ogranicz kontekst do okresu istnienia komponentu.
Nowe DbContext instancje
Najszybszym sposobem utworzenia nowego wystąpienia DbContext jest użycie new do utworzenia nowego wystąpienia. Istnieją jednak scenariusze wymagające rozwiązania dodatkowych zależności:
- Korzystanie z
DbContextOptions, aby skonfigurować kontekst. - Używanie parametrów połączenia dla DbContext, na przykład podczas korzystania z modelu Identity platformy ASP.NET Core. Aby uzyskać więcej informacji, zobacz Multi-tenancy (EF Core dokumentacja).
Ostrzeżenie
Nie przechowuj sekretów aplikacji, parametrów połączenia, poświadczeń, haseł, osobistych numerów identyfikacyjnych (PIN), prywatnego kodu C#/.NET ani kluczy prywatnych/tokenów w kodzie po stronie klienta, który jest zawsze niezabezpieczony. W środowiskach testowych/przejściowych i produkcyjnych kod Blazor po stronie 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 przechowywanie poufnych danych i poświadczeń.
Zalecanym podejściem do utworzenia nowego DbContext elementu z zależnościami jest użycie fabryki. EF Core Platforma .NET 5 lub nowsza udostępnia wbudowany mechanizm do tworzenia nowych kontekstów.
W wersjach platformy .NET wcześniejszych niż .NET 5, 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 powyższej fabryce:
- ActivatorUtilities.CreateInstance rozwiązuje wszelkie zależności za pośrednictwem dostawcy usług.
- IDbContextFactory<TContext> jest dostępny w programie EF Core .NET 5 lub nowszym, więc poprzedni 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 komponentu. Dzięki temu można używać go jako jednostki pracy i korzystać z wbudowanych funkcji, takich jak śledzenie zmian i rozwiązywanie konfliktów 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łącz rejestrowanie poufnych danych
EnableSensitiveDataLogging zawiera dane aplikacji w komunikatach o wyjątkach i rejestrowaniu przez platformę. Zarejestrowane dane mogą zawierać wartości przypisane do właściwości instancji encji i wartości parametrów dla poleceń wysyłanych do bazy danych. Rejestrowanie danych za pomocą EnableSensitiveDataLogging jest zagrożeniem bezpieczeństwa, ponieważ może ujawniać hasła i inne dane umożliwiające identyfikację osoby (PII) podczas rejestrowania instrukcji SQL wykonywanych względem bazy danych.
Zalecamy włączenie EnableSensitiveDataLogging tylko do lokalnego tworzenia i testowania:
if (builder.Environment.IsDevelopment())
{
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"));
}