Aracılığıyla paylaş


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

Not

Bu, bu makalenin en son sürümü değildir. Geçerli sürüm için bu makalenin .NET 8 sürümüne bakın.

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.

Önemli

Bu bilgiler, ticari olarak piyasaya sürülmeden önce önemli ölçüde değiştirilebilen bir yayın öncesi ürünle ilgilidir. Burada verilen bilgilerle ilgili olarak Microsoft açık veya zımni hiçbir garanti vermez.

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.jsonyapı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ın Loading = 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ğı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

Filterseklenen 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

Filterseklenen 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 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 EnableSensitiveDataLogginggü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