Aracılığıyla paylaş


DbContext Ömrü, Yapılandırma ve Başlatma

Bu makalede bir DbContext örneğinin başlatılması ve yapılandırılması için temel desenlere yer verilmiştir.

DbContext ömrü

Bir DbContext için ömür, örnek oluşturulduğunda başlar ve örnek atıldığında sona erer. DbContext örneği, tek biriş birimidir. Bu da DbContext örneğinin ömrünün genellikle çok kısa olduğu anlamına gelir.

Bahşiş

Yukarıdaki bağlantıda verilen sayfadan ve Martin Fowler’dan alıntı yapacak olursak: “İş Birimi, bir iş işlemi sırasında yaptığınız ve veritabanını etkileyebilecek olan her şeyi izler. İşiniz bittiğinde, çalışmanızın bir sonucu olarak veritabanını değiştirmek için yapılması gereken her şeyi bulur.”

Entity Framework Core (EF Core) kullanıldığında tipik bir iş birimi şunları içerir:

Önemli

  • DbContext örneğini kullandıktan sonra atmak çok önemlidir. Bu, hem yönetilmeyen kaynakların serbest kalmasını hem de örneğe başvurulmaya devam edilebilmesi durumunda bellek sızıntılarını önlemek için tüm olayların veya diğer kancaların kaydının kaldırılmasını sağlar.
  • DbContext, iş parçacığı açısından güvenli değildir. Bağlamları iş parçacıkları arasında paylaşmayın. Bağlam örneğini kullanmaya devam etmeden önce tüm zaman uyumsuz çağrıları beklettiğinizden emin olun.
  • EF Core kodu tarafından oluşturulan bir InvalidOperationException, bağlamı kurtarılamaz duruma getirebilir. Bu tür özel durumlar bir program hatası olduğunu gösterir ve kurtarılacak şekilde tasarlanmamıştır.

ASP.NET Core için bağımlılık eklemede DbContext

Çoğu web uygulamasında her HTTP isteği, tek bir iş birimine karşılık gelir. Bu da bağlam ömrünü isteğin ömrüne uydurur ve web uygulamaları için iyi bir varsayılan düzen sunar.

ASP.NET Core uygulamaları bağımlılık ekleme kullanılarak yapılandırılır. EF Core, Startup.cs öğesinin ConfigureServices yönteminde AddDbContext kullanılarak bu yapılandırmaya eklenebilir. Örnek:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddDbContext<ApplicationDbContext>(
        options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
}

Bu örnek, ASP.NET Core uygulama hizmeti sağlayıcısına (bağımlılık ekleme kapsayıcısı) kapsamlı hizmet olarak ApplicationDbContext adlı bir DbContext alt sınıfı kaydeder. Bağlam, SQL Server veritabanı sağlayıcısını kullanacak şekilde yapılandırılır ve bağlantı dizesini ASP.NET Core yapılandırmasından okur. Genellikle AddDbContext çağrısının ConfigureServices içindeki konumu önemli değildir.

ApplicationDbContext sınıfı bir DbContextOptions<ApplicationDbContext> parametresiyle ortak bir oluşturucuyu kullanıma sunmalıdır. Bağlam yapılandırması bu şekilde AddDbContext kaynağından DbContext hedefine geçirilir. Örnek:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

ApplicationDbContext bu şekilde oluşturucu ekleme yoluyla ASP.NET Core denetleyicilerde veya diğer hizmetlerde kullanılabilir. Örnek:

public class MyController
{
    private readonly ApplicationDbContext _context;

    public MyController(ApplicationDbContext context)
    {
        _context = context;
    }
}

Nihai sonuç, her istek için oluşturulan ve istek sona erdiğinde atılmadan önce bir iş birimi gerçekleştirmek üzere denetleyiciye geçirilen bir ApplicationDbContext örneği olur.

Yapılandırma seçenekleri hakkında daha fazla bilgi edinmek için bu makaleyi okumaya devam edin. Ayrıca, ASP.NET Core’da yapılandırma ve bağımlılık ekleme hakkında daha fazla bilgi için bkz. ASP.NET Core’da uygulama başlatma ve ASP.NET Core’da bağımlılık ekleme.

“new” ile basit DbContext başlatma

DbContext örnekleri normal .NET yöntemiyle, örneğin C# dilinde new ile oluşturulabilir. Yapılandırma, OnConfiguring yöntemi geçersiz kılınarak veya oluşturucuya seçenekler geçirilerek gerçekleştirilebilir. Örnek:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Bu desen, bağlantı dizesi gibi yapılandırma verilerini DbContext oluşturucusu aracılığıyla geçirmeyi de kolaylaştırır. Örnek:

public class ApplicationDbContext : DbContext
{
    private readonly string _connectionString;

    public ApplicationDbContext(string connectionString)
    {
        _connectionString = connectionString;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(_connectionString);
    }
}

Alternatif olarak DbContextOptionsBuilder daha sonra DbContext oluşturucusuna geçirilen bir DbContextOptions nesnesi oluşturmak için kullanılabilir. Bu, bağımlılık ekleme için yapılandırılmış bir DbContext öğesinin de açıkça yapılandırılmasını sağlar. Örneğin, yukarıdaki ASP.NET Core web uygulamaları için tanımlanan ApplicationDbContext kullanılırken:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

DbContextOptions oluşturulabilir ve oluşturucu açıkça çağrılabilir:

var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
    .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0")
    .Options;

using var context = new ApplicationDbContext(contextOptions);

DbContext fabrikası kullanma (örn. Blazor için)

Bazı uygulama türleri (örn. ASP.NET Core Blazor) bağımlılık eklemeyi kullanır ancak istenen DbContext ömrüyle uyumlu bir hizmet kapsamı oluşturmaz. Böyle bir uyumluluk mevcut olsa bile uygulamanın bu kapsamda birden çok iş birimi gerçekleştirmesi gerekebilir. Örneğin tek bir HTTP isteğinde birden çok iş birimi olması gerekebilir.

Bu gibi durumlarda DbContext örneklerinin oluşturulması amacıyla bir fabrikayı kaydetmek için AddDbContextFactory kullanılabilir. Örnek:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContextFactory<ApplicationDbContext>(
        options =>
            options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"));
}

ApplicationDbContext sınıfı bir DbContextOptions<ApplicationDbContext> parametresiyle ortak bir oluşturucuyu kullanıma sunmalıdır. Bu, yukarıdaki geleneksel ASP.NET Core bölümünde kullanılan desenle aynıdır.

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

DbContextFactory fabrikası daha sonra oluşturucu ekleme yoluyla diğer hizmetlerde kullanılabilir. Örnek:

private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;

public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
    _contextFactory = contextFactory;
}

Eklenen fabrika daha sonra hizmet kodunda DbContext örnekleri oluşturmak için kullanılabilir. Örnek:

public void DoSomething()
{
    using (var context = _contextFactory.CreateDbContext())
    {
        // ...
    }
}

Bu şekilde oluşturulan DbContext örneklerinin, uygulamanın hizmet sağlayıcısı tarafından yönetilmediğine ve bu nedenle uygulama tarafından atılması gerektiğini unutmayın.

Blazor ile EF Core kullanma hakkında daha fazla bilgi için bkz. ASP.NET Core Blazor Server ve Entity Framework Core.

DbContextOptions

Tüm DbContext yapılandırmalarının başlangıç noktası DbContextOptionsBuilder olur. Bu oluşturucuya ulaşmanın üç farklı yöntemi vardır:

  • AddDbContext ve ilgili yöntemlerle
  • OnConfiguring ile
  • new ile açıkça oluşturma

Bu örneklerin her biri önceki bölümlerde gösterilmiştir. Oluşturucunun kaynağından bağımsız olarak aynı yapılandırma uygulanabilir. Ayrıca bağlamın oluşturulma şeklinden bağımsız olarak her zaman OnConfiguring çağrısı yapılır. Bu, AddDbContext kullanılırken bile ek yapılandırma gerçekleştirmek için OnConfiguring kullanılabileceğini gösterir.

Veritabanı sağlayıcısını yapılandırma

Her DbContext örneği, tek bir veritabanı sağlayıcısı kullanacak şekilde yapılandırılmalıdır. (Bir DbContext alt türünün farklı örnekleri, farklı veritabanı sağlayıcılarıyla kullanılabilir ancak tek bir örnek yalnızca bir tanesini kullanmalıdır.) Veritabanı sağlayıcısı belirli bir Use* çağrısı kullanılarak yapılandırılır. Örneğin SQL Server veritabanı sağlayıcısını kullanmak için:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Bu Use* yöntemleri, veritabanı sağlayıcısı tarafından uygulanan uzantı yöntemleridir. Bu, uzantı yönteminin kullanılabilmesi için önce veritabanı sağlayıcısı NuGet paketinin yüklenmesi gerektiği anlamına gelir.

Bahşiş

EF Core veritabanı sağlayıcıları, uzantı yöntemlerini kapsamlı bir şekilde kullanır. Derleyici bir yöntemin bulunamadığını belirtiyorsa sağlayıcının NuGet paketinin yüklü olduğundan ve kodunuzda using Microsoft.EntityFrameworkCore; olduğundan emin olun.

Aşağıdaki tabloda yaygın veritabanı sağlayıcıları için örnekler verilmiştir.

Veritabanı sistemi Örnek yapılandırma NuGet paketi
SQL Server veya Azure SQL .UseSqlServer(connectionString) Microsoft.EntityFrameworkCore.SqlServer
Azure Cosmos DB .UseCosmos(connectionString, databaseName) Microsoft.EntityFrameworkCore.Cosmos
SQLite .UseSqlite(connectionString) Microsoft.EntityFrameworkCore.Sqlite
EF Core bellek içi veritabanı .UseInMemoryDatabase(databaseName) Microsoft.EntityFrameworkCore.InMemory
PostgreSQL* .UseNpgsql(connectionString) Npgsql.EntityFrameworkCore.PostgreSQL
MySQL/MariaDB* .UseMySql(connectionString) Pomelo.EntityFrameworkCore.MySql
Oracle* .UseOracle(connectionString) Oracle.EntityFrameworkCore

*Bu veritabanı sağlayıcıları Microsoft tarafından gönderilmez. Veritabanı sağlayıcıları hakkında daha fazla bilgi için bkz. Veritabanı Sağlayıcıları.

Uyarı

EF Core bellek içi veritabanı, üretim ortamında kullanılmak üzere tasarlanmamıştır. Ayrıca test için bile en iyi seçenek olmayabilir. Daha fazla bilgi için bkz. EF Core Kullanılan Test Kodu.

EF Core ile bağlantı dizelerini kullanma hakkında daha fazla bilgi için bkz. Bağlantı Dizeleri.

Veritabanı sağlayıcısına özgü isteğe bağlı yapılandırma, sağlayıcıya özgü ek bir oluşturucuda gerçekleştirilir. Örneğin, Azure SQL ortamına bağlanırken bağlantı dayanıklılığı için yeniden denemeleri yapılandırma amacıyla EnableRetryOnFailure kullanma:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(
                @"Server=(localdb)\mssqllocaldb;Database=Test",
                providerOptions => { providerOptions.EnableRetryOnFailure(); });
    }
}

Bahşiş

SQL Server ve Azure SQL için aynı veritabanı sağlayıcısı kullanılır. Ancak SQL Azure ortamına bağlanırken bağlantı dayanıklılığının kullanılması önerilir.

Sağlayıcıya özgü yapılandırma hakkında daha fazla bilgi için bkz. Veritabanı Sağlayıcıları.

Diğer DbContext yapılandırmaları

Diğer DbContext yapılandırmaları Use* çağrısından önce veya sonra zincirlenebilir (bir fark olmaz). Örneğin hassas veri günlüğü kaydını açmak için:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .EnableSensitiveDataLogging()
            .UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
    }
}

Aşağıdaki tabloda DbContextOptionsBuilder üzerinde çağrılan yaygın yöntemlerin örnekleri verilmiştir.

DbContextOptionsBuilder yöntemi Ne yapar? Daha fazla bilgi edinin
UseQueryTrackingBehavior Sorgular için varsayılan izleme davranışını ayarlar Sorgu İzleme Davranışı
LogTo EF Core günlüklerini edinmenin basit bir yolu Günlüğe Kaydetme, Olaylar ve Tanılamalar
UseLoggerFactory Bir Microsoft.Extensions.Logging fabrikasını kaydeder Günlüğe Kaydetme, Olaylar ve Tanılamalar
EnableSensitiveDataLogging Uygulama verilerini özel durumlara ve günlüğe dahil eder Günlüğe Kaydetme, Olaylar ve Tanılamalar
EnableDetailedErrors Daha ayrıntılı sorgu hataları (performans daha düşük olur) Günlüğe Kaydetme, Olaylar ve Tanılamalar
ConfigureWarnings Uyarıları ve diğer olayları yoksayar veya atar Günlüğe Kaydetme, Olaylar ve Tanılamalar
AddInterceptors EF Core durdurucularını kaydeder Günlüğe Kaydetme, Olaylar ve Tanılamalar
UseLazyLoadingProxies Geç yükleme için dinamik ara sunucuları kullanır Geç Yükleme
UseChangeTrackingProxies Değişiklik izleme için dinamik ara sunucuları kullanır Çok yakında...

Dekont

UseLazyLoadingProxies ve UseChangeTrackingProxies, Microsoft.EntityFrameworkCore.Proxies NuGet paketine dahil olan genişletme yöntemleridir. Bu tür bir “. UseSomething()” çağrısı, diğer paketlerde bulunan EF Core uzantılarını yapılandırmanın ve/veya kullanmak için önerilen yöntemdir.

DbContextOptions ile DbContextOptions<TContext>

DbContextOptions kabul eden çoğu DbContext alt sınıfı için genelDbContextOptions<TContext> varyasyonu kullanılmalıdır. Örnek:

public sealed class SealedApplicationDbContext : DbContext
{
    public SealedApplicationDbContext(DbContextOptions<SealedApplicationDbContext> contextOptions)
        : base(contextOptions)
    {
    }
}

Bu, birden çok DbContext alt türü kaydedildiğinde bile belirli DbContext alt türü için doğru seçeneklerin bağımlılık eklemeden çözümlenmesini sağlar.

Bahşiş

DbContext örneğinizin korumalı olması gerekmez ancak en iyi yöntem, devralınmak üzere tasarlanmamış sınıflarda bunu yapmaktır.

Ancak DbContext alt türünün devralınması amaçlanıyorsa, genel olmayan bir DbContextOptions alan korumalı bir oluşturucuyu kullanıma sunmalıdır. Örnek:

public abstract class ApplicationDbContextBase : DbContext
{
    protected ApplicationDbContextBase(DbContextOptions contextOptions)
        : base(contextOptions)
    {
    }
}

Bu, birden çok somut alt sınıfın farklı genel DbContextOptions<TContext> örneklerini kullanarak bu temel oluşturucuyu çağırmasına olanak tanır. Örnek:

public sealed class ApplicationDbContext1 : ApplicationDbContextBase
{
    public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
        : base(contextOptions)
    {
    }
}

public sealed class ApplicationDbContext2 : ApplicationDbContextBase
{
    public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
        : base(contextOptions)
    {
    }
}

Bunun doğrudan DbContext öğesinden devralma ile tam olarak aynı desen olduğuna dikkat edin. Başka bir deyişle DbContext oluşturucusunun kendisi bu nedenle genel olmayan bir DbContextOptions kabul eder.

Hem örnek oluşturma hem de devralma için kullanılan bir DbContext alt sınıfı, her iki oluşturucu türünü de kullanıma sunmalıdır. Örnek:

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> contextOptions)
        : base(contextOptions)
    {
    }

    protected ApplicationDbContext(DbContextOptions contextOptions)
        : base(contextOptions)
    {
    }
}

Tasarım zamanı DbContext yapılandırması

EF Core geçişleri için olanlar gibi EF Core tasarım zamanı araçlarının, uygulamanın varlık türleri ve bunların bir veritabanı şemasıyla nasıl eşleneceği hakkındaki ayrıntıları toplamak üzere bir DbContext türünün çalışan örneğini bulabilmesi ve oluşturabilmesi gerekir. Araç, çalışma zamanındaki yapılandırmaya benzer şekilde kolayca DbContext oluşturabildiği sürece bu işlem otomatik gerçekleştirilebilir.

DbContext için gerekli yapılandırma bilgilerini sağlayan tüm desenler çalışma zamanında çalışabilir ancak tasarım zamanında bir DbContext kullanılmasını gerektiren araçlar yalnızca sınırlı sayıda desenle çalışabilir. Bunlar Tasarım Zamanı Bağlamı Oluşturma sayfasında daha ayrıntılı olarak ele alınmıştır.

DbContext iş parçacığı sorunlarını önleme

Entity Framework Core, aynı DbContext örneğinde birden çok paralel işlemin çalıştırılmasını desteklemez. Buna hem zaman uyumsuz sorguların paralel yürütülmesi hem de birden çok iş parçacığından açık eş zamanlı kullanım dahildir. Bu nedenle, zaman uyumsuz çağrılarla her zaman await kullanın veya paralel olarak yürütülen işlemler için ayrı DbContext örnekleri kullanın.

EF Core bir DbContext örneğini eşzamanlı olarak kullanma girişimi algıladığında şuna benzer bir ileti içeren bir InvalidOperationException görürsünüz:

Önceki işlem tamamlanmadan önce bu bağlamda ikinci bir işlem başlatıldı. Bu durum genellikle aynı DbContext örneğini kullanan farklı iş parçacıklarından kaynaklanır ancak örnek üyelerinin iş parçacığı açısından güvenli olacağına dair bir garanti yoktur.

Eş zamanlı erişim algılanmadığında tanımsız davranışa, uygulama kilitlenmelerine ve veri bozulmasına neden olabilir.

Aynı DbContext örneğinde yanlışlıkla eşzamanlı erişime neden olabilecek yaygın hatalar vardır:

Zaman uyumsuz işlem tuzakları

Zaman uyumsuz yöntemler, EF Core’un veritabanına engelleyici olmayan bir şekilde erişen işlemler başlatmasını sağlar. Ancak çağıran, bu yöntemlerden birinin tamamlanmasını beklemezse ve DbContext üzerinde diğer işlemleri gerçekleştirmeye devam ederse DbContext durumu bozulabilir (büyük olasılıkla bozulur).

EF Core zaman uyumsuz yöntemlerini her zaman hemen bekleyin.

Bağımlılık ekleme yoluyla DbContext örneklerini örtük olarak paylaşma

AddDbContext genişletme yöntemi, DbContext türlerini varsayılan olarak kapsamı belirlenmiş bir ömürle kaydeder.

Her istemci isteğini belirli bir anda yalnızca bir iş parçacığının yürütmesi ve her isteğin ayrı bir bağımlılık ekleme kapsamı (ve dolayısıyla ayrı bir DbContext örneği) alması nedeniyle çoğu ASP.NET Core uygulamasında eşzamanlı erişim sorunları yaşanmaz. Blazor Server barındırma modelinde Blazor kullanıcı bağlantı hattını korumak için bir mantıksal istek kullanılır ve bu nedenle varsayılan ekleme kapsamı kullanıldığında kullanıcı bağlantı hattı başına yalnızca bir kapsamlı DbContext örneği kullanılabilir.

Birden çok iş parçacığını açıkça paralel olarak yürüten tüm kodlar, DbContext örneklerine hiçbir zaman eşzamanlı olarak erişilmemesini sağlamalıdır.

Bağımlılık ekleme kullanıldığında bunu yapmak için bağlam kapsamlı olarak kaydedilebilir ve her iş parçacığı için kapsamlar oluşturarak (IServiceScopeFactory kullanarak) veya DbContext geçici olarak kaydederek (ServiceLifetime parametresini alan AddDbContext aşırı yükleme kullanılarak) gerçekleştirilebilir.

Ek kaynaklar