Entity Framework Core Blazor ile ASP.NET Core (EF Core)
Uyarı
ASP.NET Core'un bu sürümü artık desteklenmiyor. Daha fazla bilgi için bkz . .NET ve .NET Core Destek İlkesi. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.
Bu makalede, sunucu tarafı Blazor uygulamalarda Entity Framework Core'un (EF Core) nasıl kullanılacağı açıklanmaktadır.
Sunucu tarafı Blazor durum bilgisi olan bir uygulama çerçevesidir. Uygulama sunucuyla devam eden bir bağlantı tutar ve kullanıcının durumu sunucunun belleğinde bir bağlantı hattında tutulur. Kullanıcı durumunun bir örneği, bağlantı hattı kapsamındaki bağımlılık ekleme (DI) hizmet örneklerinde tutulan verilerdir. Sağlayan benzersiz uygulama modeli Blazor , Entity Framework Core kullanmak için özel bir yaklaşım gerektirir.
Not
Bu makale, sunucu tarafı Blazor uygulamalarında ele alırEF Core. Blazor WebAssembly uygulamalar, çoğu doğrudan veritabanı bağlantısını engelleyen bir WebAssembly korumalı alanında çalışır. içinde Blazor WebAssembly çalıştırmak EF Core bu makalenin kapsamının dışındadır.
Bu kılavuz, bir Blazor Web Uygulamasında etkileşimli sunucu tarafı işlemeyi (etkileşimli SSR) benimseyen bileşenler için geçerlidir.
Bu kılavuz barındırılan Blazor WebAssembly bir çözümün Server
veya Blazor Server uygulamanın projesi için geçerlidir.
Örnek uygulama
Örnek uygulama, kullanan EF Coresunucu tarafı Blazor uygulamalar için bir başvuru olarak oluşturulmuştu. Örnek uygulama sıralama ve filtreleme, silme, ekleme ve güncelleştirme işlemlerini içeren bir kılavuz içerir. Örnek, iyimser eşzamanlılığı işlemek için öğesinin kullanımını EF Core gösterir.
Örnek kodu görüntüleme veya indirme (indirme): Benimsediğiniz .NET sürümüyle eşleşen klasörü seçin. sürüm klasöründe adlı BlazorWebAppEFCore
örneğe erişin.
Örnek kodu görüntüleme veya indirme (indirme): Benimsediğiniz .NET sürümüyle eşleşen klasörü seçin. sürüm klasöründe adlı BlazorServerEFCoreSample
örneğe erişin.
Örnek, herhangi bir platformda kullanılabilmesi için yerel bir SQLite veritabanı kullanır. Örnek ayrıca veritabanı günlüğünü oluşturulan SQL sorgularını gösterecek şekilde yapılandırıyor. Bu, içinde appsettings.Development.json
yapılandırılır:
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
Kılavuz, ekleme ve görüntüleme bileşenleri, her işlem için bir bağlamın oluşturulduğu "işlem başına bağlam" desenini kullanır. Düzenleme bileşeni, her bileşen için bir bağlamın oluşturulduğu "bileşen başına bağlam" desenini kullanır.
Not
Bu konudaki kod örneklerinden bazıları, gösterilmeyen ad alanları ve hizmetler gerektirir. Gerekli @using
ve @inject
örneklere yönelik yönergeler de dahil olmak üzere tam olarak çalışan kodu incelemek için Razor örnek uygulamaya bakın.
Veritabanı erişimi
EF CoreDbContext, veritabanı erişimini yapılandırmanın ve bir iş birimi olarak davranmanın bir aracı olarak çalışır. EF Corebağlamı AddDbContext varsayılan olarak kapsamlı bir hizmet olarak kaydeden ASP.NET Core uygulamaları için uzantıyı sağlar. Sunucu tarafı Blazor uygulamalarda, kapsamlı hizmet kayıtları sorunlu olabilir çünkü örnek kullanıcının bağlantı hattındaki bileşenler arasında paylaşılır. DbContext iş parçacığı güvenli değildir ve eşzamanlı kullanım için tasarlanmamıştır. Mevcut yaşam süreleri şu nedenlerle uygun değil:
- Singleton , uygulamanın tüm kullanıcıları arasında durumu paylaşır ve uygunsuz eşzamanlı kullanıma yol açar.
- Kapsamı belirlenmiş (varsayılan), aynı kullanıcı için bileşenler arasında benzer bir sorun oluşturur.
- Geçici olarak istek başına yeni bir örnek elde edilebilir, ancak bileşenler uzun ömürlü olabileceği için bu, hedeflenenden daha uzun süreli bir bağlamla sonuçlanır.
Aşağıdaki öneriler, sunucu tarafı Blazor uygulamalarda kullanmaya EF Core yönelik tutarlı bir yaklaşım sağlamak üzere tasarlanmıştır.
Varsayılan olarak, işlem başına bir bağlam kullanmayı göz önünde bulundurun. Bağlam hızlı ve düşük ek yük örnekleme için tasarlanmıştır:
using var context = new MyContext(); return await context.MyEntities.ToListAsync();
Birden çok eşzamanlı işlemi önlemek için bir bayrak kullanın:
if (Loading) { return; } try { Loading = true; ... } finally { Loading = false; }
bloktaki
try
satırınLoading = true;
arkasına işlemleri yerleştirin.İş parçacığı güvenliği sorun oluşturmadığından, mantığın yüklenmesi için veritabanı kayıtlarının kilitlenmesi gerekmez. Yükleme mantığı kullanıcı arabirimi denetimlerini devre dışı bırakmak için kullanılır, böylece kullanıcılar yanlışlıkla düğmeler seçmez veya veriler getirilirken alanları güncelleştirmez.
Birden çok iş parçacığının aynı kod bloğuna erişme olasılığı varsa, bir fabrika ekleyin ve işlem başına yeni bir örnek oluşturun. Aksi takdirde, bağlamı eklemek ve kullanmak genellikle yeterlidir.
'nin değişiklik izleme veya eşzamanlılık denetiminden EF Coreyararlanan daha uzun süreli işlemler için, bağlamı bileşenin kullanım ömrüyle kapsamına alın.
Yeni DbContext
örnekler
Yeni DbContext örnek oluşturmanın en hızlı yolu, kullanarak yeni bir örnek oluşturmaktır new
. Ancak, ek bağımlılıkların çözülmesini gerektiren senaryolar vardır:
- Bağlamı yapılandırmak için kullanma
DbContextOptions
. - ASP.NET Core'un modelini kullandığınızda olduğu gibi başına DbContextbir bağlantı dizesi kullanma.Identity Daha fazla bilgi için bkz . Çoklu kiracı (EF Core belgeler).
Bağımlılıklarla yeni DbContext bir oluşturmanın önerilen yaklaşımı bir fabrika kullanmaktır. EF Core 5.0 veya üzeri, yeni bağlamlar oluşturmak için yerleşik bir fabrika sağlar.
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);
}
}
Önceki fabrikada:
- ActivatorUtilities.CreateInstance hizmet sağlayıcısı aracılığıyla tüm bağımlılıkları karşılar.
IDbContextFactory
ASP.NET Core 5.0 veya sonraki sürümlerinde kullanılabilir EF Core , bu nedenle arabirim ASP.NET Core 3.x için örnek uygulamada uygulanır.
Aşağıdaki örnekte SQLite yapılandırılır ve veri günlüğü etkinleştirildi. Kod, veritabanı fabrikasını DI için yapılandırmak ve varsayılan seçenekleri sağlamak için bir uzantı yöntemi (AddDbContextFactory
) kullanır:
builder.Services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
builder.Services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
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"));
services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
Fabrika bileşenlere eklenir ve yeni DbContext
örnekler oluşturmak için kullanılır.
Örnek uygulamanın giriş sayfasında bileşenine IDbContextFactory<ContactContext>
eklenir:
@inject IDbContextFactory<ContactContext> DbFactory
DbContext
yöntemindeki bir kişiyi DeleteContactAsync
silmek için fabrika (DbFactory
) kullanılarak oluşturulur:
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
Filters.Loading = true;
if (Wrapper is not null && context.Contacts is not null)
{
var contact = await context.Contacts
.FirstAsync(c => c.Id == Wrapper.DeleteRequestId);
if (contact is not null)
{
context.Contacts?.Remove(contact);
await context.SaveChangesAsync();
}
}
Filters.Loading = false;
await ReloadAsync();
}
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
Filters.Loading = true;
if (Wrapper is not null && context.Contacts is not null)
{
var contact = await context.Contacts
.FirstAsync(c => c.Id == Wrapper.DeleteRequestId);
if (contact is not null)
{
context.Contacts?.Remove(contact);
await context.SaveChangesAsync();
}
}
Filters.Loading = false;
await ReloadAsync();
}
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
Filters.Loading = true;
if (Wrapper is not null && context.Contacts is not null)
{
var contact = await context.Contacts
.FirstAsync(c => c.Id == Wrapper.DeleteRequestId);
if (contact is not null)
{
context.Contacts?.Remove(contact);
await context.SaveChangesAsync();
}
}
Filters.Loading = false;
await ReloadAsync();
}
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
Filters.Loading = true;
var contact = await context.Contacts.FirstAsync(
c => c.Id == Wrapper.DeleteRequestId);
if (contact != null)
{
context.Contacts.Remove(contact);
await context.SaveChangesAsync();
}
Filters.Loading = false;
await ReloadAsync();
}
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
Filters.Loading = true;
var contact = await context.Contacts.FirstAsync(
c => c.Id == Wrapper.DeleteRequestId);
if (contact != null)
{
context.Contacts.Remove(contact);
await context.SaveChangesAsync();
}
Filters.Loading = false;
await ReloadAsync();
}
Not
Filters
eklenen bir ve Wrapper
bileşene yönelik GridWrapper
bir bileşen başvurusudur.IContactFilters
Örnek uygulamadaki Home
bileşene (Components/Pages/Home.razor
) bakın.
Not
Filters
eklenen bir ve Wrapper
bileşene yönelik GridWrapper
bir bileşen başvurusudur.IContactFilters
Örnek uygulamadaki Index
bileşene (Pages/Index.razor
) bakın.
Bileşen ömrünün kapsamı
Bir bileşenin ömrü boyunca var olan bir oluşturmak DbContext isteyebilirsiniz. Bu, bunu bir iş birimi olarak kullanmanıza ve değişiklik izleme ve eşzamanlılık çözümlemesi gibi yerleşik özelliklerden yararlanmanıza olanak tanır.
Bir bağlam oluşturmak ve bileşenin kullanım ömrü boyunca izlemek için fabrikayı kullanabilirsiniz. İlk olarak, bileşeninde EditContact
gösterildiği gibi fabrikayı uygulayın IDisposable ve enjekte edin (Components/Pages/EditContact.razor
):
Bir bağlam oluşturmak ve bileşenin kullanım ömrü boyunca izlemek için fabrikayı kullanabilirsiniz. İlk olarak, bileşeninde EditContact
gösterildiği gibi fabrikayı uygulayın IDisposable ve enjekte edin (Pages/EditContact.razor
):
@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
Örnek uygulama, bileşen atıldığında bağlamın atılmasını sağlar:
public void Dispose()
{
Context?.Dispose();
}
public void Dispose()
{
Context?.Dispose();
}
public void Dispose()
{
Context?.Dispose();
}
public void Dispose()
{
Context?.Dispose();
}
public void Dispose()
{
Context?.Dispose();
}
Son olarak, OnInitializedAsync
yeni bir bağlam oluşturmak için geçersiz kılınmış olur. Örnek uygulamada, OnInitializedAsync
kişiyi aynı yöntemde yükler:
protected override async Task OnInitializedAsync()
{
Busy = true;
try
{
Context = DbFactory.CreateDbContext();
if (Context is not null && Context.Contacts is not null)
{
var contact = await Context.Contacts.SingleOrDefaultAsync(c => c.Id == ContactId);
if (contact is not null)
{
Contact = contact;
}
}
}
finally
{
Busy = false;
}
await base.OnInitializedAsync();
}
protected override async Task OnInitializedAsync()
{
Busy = true;
try
{
Context = DbFactory.CreateDbContext();
if (Context is not null && Context.Contacts is not null)
{
var contact = await Context.Contacts.SingleOrDefaultAsync(c => c.Id == ContactId);
if (contact is not null)
{
Contact = contact;
}
}
}
finally
{
Busy = false;
}
await base.OnInitializedAsync();
}
protected override async Task OnInitializedAsync()
{
Busy = true;
try
{
Context = DbFactory.CreateDbContext();
if (Context is not null && Context.Contacts is not null)
{
var contact = await Context.Contacts.SingleOrDefaultAsync(c => c.Id == ContactId);
if (contact is not null)
{
Contact = contact;
}
}
}
finally
{
Busy = false;
}
await base.OnInitializedAsync();
}
protected override async Task OnInitializedAsync()
{
Busy = true;
try
{
Context = DbFactory.CreateDbContext();
Contact = await Context.Contacts
.SingleOrDefaultAsync(c => c.Id == ContactId);
}
finally
{
Busy = false;
}
await base.OnInitializedAsync();
}
protected override async Task OnInitializedAsync()
{
Busy = true;
try
{
Context = DbFactory.CreateDbContext();
Contact = await Context.Contacts
.SingleOrDefaultAsync(c => c.Id == ContactId);
}
finally
{
Busy = false;
}
await base.OnInitializedAsync();
}
Yukarıdaki örnekte:
Busy
olarak ayarlandığındatrue
, zaman uyumsuz işlemler başlayabilir.Busy
yeniden olarak ayarlandığındafalse
, zaman uyumsuz işlemler tamamlanmalıdır.- Bir bloğa ek hata işleme mantığı
catch
yerleştirin.
Hassas veri günlüğünü etkinleştirme
EnableSensitiveDataLogging özel durum iletilerine ve çerçeve günlüğüne uygulama verilerini içerir. Günlüğe kaydedilen veriler, varlık örneklerinin özelliklerine atanan değerleri ve veritabanına gönderilen komutlar için parametre değerlerini içerebilir. Veritabanında yürütülen SQL deyimlerini günlüğe kaydederken parolaları ve diğer Kişisel Bilgileri (PII) kullanıma sunma ihtimalinden, ile verilerin EnableSensitiveDataLogging günlüğe kaydedilmesi bir güvenlik riskidir.
Yalnızca geliştirme ve test için etkinleştirmenizi EnableSensitiveDataLogging öneririz:
#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
Ek kaynaklar
ASP.NET Core