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. ÖrnekDbContext
, tek bir çalışma birimi için kullanılacak şekilde tasarlanmıştır. Bu da DbContext
örneğinin ömrünün genellikle çok kısa olduğu anlamına gelir.
İpucu
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:
- Bir
DbContext
örneğinin oluşturulması - Varlık örneklerinin bağlama göre izlenmesi. Varlıklar tarafından izlenir
- İş kuralını uygulamak için gerektiğinde izlenen varlıklarda değişiklikler yapılır
- SaveChanges veya SaveChangesAsync çağrıldığında. EF Core, yapılan değişiklikleri algılar ve bunları veritabanına yazar.
DbContext
örneği atıldığında
Ö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. Örneğin:
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. Örneğin:
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. Örneğin:
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. Örneğin:
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. Örneğin:
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. Örneğin:
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. Örneğin:
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. Örneğin:
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öntemlerleOnConfiguring
ilenew
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.
İpucu
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(); });
}
}
İpucu
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... |
Not
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>
kabul DbContextOptions
eden alt sınıfların çoğu DbContext
genel DbContextOptions<TContext>
varyasyonu kullanmalıdır. Örneğin:
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.
İpucu
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. Örneğin:
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. Örneğin:
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. Örneğin:
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
- Bağımlılık ekleme hakkında daha fazla bilgi edinmek için bkz. Bağımlılık Ekleme.
- Daha fazla bilgi için bkz. Sınama.