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, içinde Blazor Web Appetkileş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.
Üretim uygulamaları için güvenli kimlik doğrulama akışı gerekiyor
Bu makalede, kullanıcı kimlik doğrulaması gerektirmeyen yerel bir veritabanı kullanılır. Üretim uygulamaları kullanılabilir en güvenli kimlik doğrulama akışını kullanmalıdır. Dağıtılan test ve üretim Blazor uygulamaları için kimlik doğrulaması hakkında daha fazla bilgi için Güvenlik ve Identitydüğüm makalelerine Blazorbakın.
Microsoft Azure hizmetleri için yönetilen kimlikleri kullanmanızı öneririz. Yönetilen kimlikler, kimlik bilgilerini uygulama kodunda depolamadan Azure hizmetlerinde güvenli bir şekilde kimlik doğrulaması yapar. Daha fazla bilgi edinmek için aşağıdaki kaynaklara bakın:
- Azure kaynakları için yönetilen kimlikler nedir? (Microsoft Entra belgeleri)
- Azure hizmetleri belgeleri
Ö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. Ancak, örnek uygulamanın veritabanı sağlayıcısı olan SQLite veritabanları için yerel veritabanı tarafından oluşturulan eşzamanlılık belirteçleri desteklenmez. Örnek uygulamayla eşzamanlılığı göstermek için, veritabanı tarafından oluşturulan eşzamanlılık belirteçlerini (örneğin, SQL Server sağlayıcısı) destekleyen farklı bir veritabanı sağlayıcısını benimseyin.
Ö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.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.
Blazor Film veritabanı uygulaması oluşturma öğreticisi
Veritabanıyla çalışmak için kullanan EF Core bir uygulama oluşturma öğretici deneyimi için bkz . Film veritabanı uygulaması derleme Blazor (Genel Bakış). Öğreticide, bir film veritabanında filmleri görüntüleyebilen ve yönetebilen bir oluşturma Blazor Web App gösterilmektedir.
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 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.
İş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 önemli değildir, bu nedenle 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).
Uyarı
Uygulama gizli dizilerini, bağlantı dizesi'leri, kimlik bilgilerini, parolaları, kişisel kimlik numaralarını (PIN'ler), özel C#/.NET kodunu veya özel anahtarları/belirteçleri her zaman güvenli olmayan istemci tarafı kodunda depolamayın. Test/hazırlama ve üretim ortamlarında, sunucu tarafı Blazor kod ve web API'leri, proje kodu veya yapılandırma dosyalarında kimlik bilgilerinin korunmasını önleyen güvenli kimlik doğrulama akışları kullanmalıdır. Yerel geliştirme testlerinin dışında, ortam değişkenleri en güvenli yaklaşım olmadığından hassas verileri depolamak için ortam değişkenlerinin kullanılmasından kaçınmanızı öneririz. Yerel geliştirme testinde gizli verilerin güvenliğini sağlamak için Gizli Dizi Yöneticisi aracı önerilir. Daha fazla bilgi için bkz . Hassas verileri ve kimlik bilgilerini güvenli bir şekilde koruma.
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"));
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.
home Örnek uygulamanın sayfasında, IDbContextFactory<ContactContext>
bileşene 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;
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();
}
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;
}
}
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;
}
}
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