Gelişmiş Performans Konuları
DbContext havuzu oluşturma
A DbContext
genellikle basit bir nesnedir: bir veritabanı işlemi oluşturma ve yok etme işlemi içermez ve çoğu uygulama bunu performans üzerinde belirgin bir etki olmadan yapabilir. Ancak, her bağlam örneği görevlerini gerçekleştirmek için gerekli çeşitli iç hizmetleri ve nesneleri ayarlar ve bunu sürekli yapma yükü yüksek performanslı senaryolarda önemli olabilir. Bu durumlarda, EF Core bağlam örneklerinizi havuza alabilir: Bağlamınızı attığınızda EF Core durumunu sıfırlar ve bir iç havuzda depolar; yeni bir örnek istendiğinde, yeni bir örnek ayarlamak yerine havuza alınan örnek döndürülür. Bağlam havuzu, bağlam kurulum maliyetlerini sürekli olarak değil, program başlangıcında yalnızca bir kez ödemenize olanak tanır.
Bağlam havuzunun, veritabanı sürücüsünde daha düşük bir düzeyde yönetilen veritabanı bağlantı havuzuna ortogonal olduğunu unutmayın.
EF Core kullanan bir ASP.NET Core uygulamasındaki tipik desen, aracılığıyla DbContextbağımlılık eklemeAddDbContextiçerir. Ardından, bu türdeki örnekler denetleyicilerdeki veya Razor Sayfalarındaki oluşturucu parametreleri aracılığıyla elde edilir.
Bağlam havuzunu etkinleştirmek için değerini ile AddDbContext
değiştirmeniz AddDbContextPool yeterlidir:
builder.Services.AddDbContextPool<WeatherForecastContext>(
o => o.UseSqlServer(builder.Configuration.GetConnectionString("WeatherForecastContext")));
poolSize
parametresiAddDbContextPool, havuz tarafından tutulan en fazla örnek sayısını ayarlar (varsayılan olarak 1024'tür). Bir kez poolSize
aşıldığında, yeni bağlam örnekleri önbelleğe alınmaz ve EF isteğe bağlı örnek oluşturmanın havuz dışı davranışına geri döner.
Karşılaştırmalar
Aşağıda, bağlam havuzuyla ve bağlam havuzu olmadan aynı makinede yerel olarak çalışan bir SQL Server veritabanından tek bir satır getirmeye yönelik karşılaştırma sonuçları yer almaktadır. Her zaman olduğu gibi sonuçlar satır sayısı, veritabanı sunucunuzdaki gecikme süresi ve diğer faktörlerle değişir. Daha da önemlisi, bu tek iş parçacıklı havuz performansını kıyaslarken, gerçek dünyada iddialı bir senaryo farklı sonuçlara sahip olabilir; herhangi bir karar vermeden önce platformunuzda karşılaştırma yapın. Kaynak kodu burada bulabilirsiniz, kendi ölçümleriniz için temel olarak kullanmaktan çekinmeyin.
Metot | NumBlogs | Ortalama | Hata | StdDev | 0. Nesil | 1. Nesil | 2. Nesil | Tahsis edilen |
---|---|---|---|---|---|---|---|---|
WithoutContextPooling | 1 | 701.6 bize | 26.62 bize | 78.48 bize | 11.7188 | - | - | 50,38 KB |
WithContextPooling | 1 | 350.1 biz | 6.80 bize | 14.64 bize | 0.9766 | - | - | 4,63 KB |
Havuza alınan bağlamlarda durumu yönetme
Bağlam havuzu, istekler arasında aynı bağlam örneğini yeniden kullanarak çalışır; bu, tekil olarak etkili bir şekilde kaydedildiği ve aynı örneğin birden çok istekte (veya DI kapsamlarında) yeniden kullanıldığı anlamına gelir. Bu, bağlam istekler arasında değişebilecek herhangi bir durumu içerdiğinde özel özen gösterilmesi gerektiği anlamına gelir. Kritik öneme sahip olan bağlam OnConfiguring
yalnızca bir kez çağrılır ( örnek bağlamı ilk oluşturulduğunda) ve bu nedenle değişiklik göstermesi gereken durumu ayarlamak için kullanılamaz (örneğin, kiracı kimliği).
Bağlam durumunu içeren tipik bir senaryo, bağlam örneğinin sorgular tarafından hesaba katılmış bir kiracı kimliğine sahip olduğu çok kiracılı bir ASP.NET Core uygulaması olacaktır (daha fazla ayrıntı için bkz. Genel Sorgu Filtreleri). Kiracı kimliğinin her web isteğiyle değişmesi gerektiğinden, bağlam havuzuyla çalışmasını sağlamak için bazı ek adımları izlememiz gerekir.
Uygulamanızın, kiracı kimliğini ve kiracıyla ilgili diğer bilgileri sarmalayan kapsamlı ITenant
bir hizmet kaydettiğinizi varsayalım:
// Below is a minimal tenant resolution strategy, which registers a scoped ITenant service in DI.
// In this sample, we simply accept the tenant ID as a request query, which means that a client can impersonate any
// tenant. In a real application, the tenant ID would be set based on secure authentication data.
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ITenant>(sp =>
{
var tenantIdString = sp.GetRequiredService<IHttpContextAccessor>().HttpContext.Request.Query["TenantId"];
return tenantIdString != StringValues.Empty && int.TryParse(tenantIdString, out var tenantId)
? new Tenant(tenantId)
: null;
});
Yukarıda belirtildiği gibi, kiracı kimliğini nereden edindiğinize özellikle dikkat edin. Bu, uygulamanızın güvenliğinin önemli bir yönüdür.
Kapsamlı ITenant
hizmetimizi aldıktan sonra, her zamanki gibi bir havuz bağlamı fabrikasını Tekil hizmet olarak kaydedin:
builder.Services.AddPooledDbContextFactory<WeatherForecastContext>(
o => o.UseSqlServer(builder.Configuration.GetConnectionString("WeatherForecastContext")));
Ardından, kaydettiğimiz Singleton fabrikasından havuza alınan bağlamı alan ve kiracı kimliğini kullanıma aldığı bağlam örneklerine ekleyen özel bir bağlam fabrikası yazın:
public class WeatherForecastScopedFactory : IDbContextFactory<WeatherForecastContext>
{
private const int DefaultTenantId = -1;
private readonly IDbContextFactory<WeatherForecastContext> _pooledFactory;
private readonly int _tenantId;
public WeatherForecastScopedFactory(
IDbContextFactory<WeatherForecastContext> pooledFactory,
ITenant tenant)
{
_pooledFactory = pooledFactory;
_tenantId = tenant?.TenantId ?? DefaultTenantId;
}
public WeatherForecastContext CreateDbContext()
{
var context = _pooledFactory.CreateDbContext();
context.TenantId = _tenantId;
return context;
}
}
Özel bağlam fabrikamızı aldıktan sonra bunu Kapsamlı hizmet olarak kaydedin:
builder.Services.AddScoped<WeatherForecastScopedFactory>();
Son olarak, Scoped fabrikamızdan eklenmek üzere bir bağlam ayarlayın:
builder.Services.AddScoped(
sp => sp.GetRequiredService<WeatherForecastScopedFactory>().CreateDbContext());
Bu noktada, denetleyicileriniz hakkında hiçbir şey bilmek zorunda kalmadan doğru kiracı kimliğine sahip bir bağlam örneğine otomatik olarak eklenir.
Bu örneğin tam kaynak koduna buradan ulaşabilirsiniz.
Not
EF Core ve ilgili hizmetleri için DbContext
iç durumu sıfırlamayı üstlenir ancak genellikle EF'nin dışındaki temel veritabanı sürücüsündeki durumu sıfırlamaz. Örneğin, el ile açar ve ADO.NET durumunu başka bir DbConnection
şekilde işlerseniz, bağlam örneğini havuza döndürmeden önce (örneğin bağlantıyı kapatarak) bu durumu geri yüklemek size bağlıdır. Bunun yapılmaması, durumun ilgisiz istekler arasında sızdırılmasına neden olabilir.
Bağlantı HavuzuYla İlgili Dikkat Edilmesi Gerekenler
Çoğu veritabanında, veritabanı işlemlerini gerçekleştirmek için uzun süreli bir bağlantı gereklidir ve bu tür bağlantıların açılması ve kapatılması pahalı olabilir. EF, bağlantı havuzunun kendisini uygulamaz, ancak veritabanı bağlantılarını yönetmek için temel alınan veritabanı sürücüsüne (örn. ADO.NET sürücü) dayanır. Bağlantı havuzu, bağlantıları tekrar tekrar açma ve kapatma yükünü azaltmak için mevcut veritabanı bağlantılarını yeniden kullanan bir istemci tarafı mekanizmasıdır. Bu mekanizma, Azure SQL Veritabanı, PostgreSQL ve diğerleri gibi EF tarafından desteklenen veritabanlarında genel olarak tutarlıdır. Ancak kaynak sınırları veya hizmet yapılandırmaları gibi veritabanına veya ortama özgü faktörler havuz verimliliğini etkileyebilir. Bağlantı havuzu genellikle varsayılan olarak etkindir ve tüm havuz yapılandırmaları bu sürücü tarafından belgelendiği gibi alt düzey sürücü düzeyinde gerçekleştirilmelidir; örneğin, ADO.NET kullanılırken, en düşük veya en büyük havuz boyutları gibi parametreler genellikle bağlantı dizesi aracılığıyla yapılandırılır.
Bağlantı havuzu, EF'nin yukarıda açıklanan DbContext
havuzuna tamamen benzerdir: alt düzey veritabanı sürücüsü veritabanı bağlantılarını havuza alırken (bağlantıları açma/kapatma ek yükünü önlemek için), EF bağlam örneklerini havuza alabilir (bağlam belleği ayırma ve başlatma ek yüklerini önlemek için). Bağlam örneği havuzda olsun ya da olmasın, EF genellikle her işlemden (örneğin, sorgu) hemen önce bağlantıları açar ve hemen ardından kapatarak havuza geri döndürür; bu, bağlantıların gereğinden fazla süreyle havuz dışında kalmasını önlemek için yapılır.
Derlenmiş sorgular
EF yürütme için bir LINQ sorgu ağacı aldığında, önce bu ağacı "derlemeli", örneğin ondan SQL üretmelidir. Bu görev ağır bir işlem olduğundan, EF sorguları sorgu ağacı şekline göre önbelleğe alır, böylece aynı yapıya sahip sorgular dahili olarak önbelleğe alınmış derleme çıkışlarını yeniden kullanır. Bu önbelleğe alma, parametre değerleri farklı olsa bile aynı LINQ sorgusunu birden çok kez yürütmenin çok hızlı olmasını sağlar.
Ancak, EF'nin iç sorgu önbelleğini kullanabilmesi için bazı görevleri gerçekleştirmesi gerekir. Örneğin, doğru önbelleğe alınmış sorguyu bulmak için sorgunuzun ifade ağacı önbelleğe alınmış sorguların ifade ağaçlarıyla özyinelemeli olarak karşılaştırılmalıdır. Bu ilk işlemenin yükü, özellikle sorgu yürütmeyle ilişkili diğer maliyetlerle (ağ G/Ç, gerçek sorgu işleme ve veritabanında disk G/Ç...) karşılaştırıldığında EF uygulamalarının çoğunda göz ardı edilebilir. Ancak, bazı yüksek performanslı senaryolarda ortadan kaldırılması istenebilir.
EF, bir LINQ sorgusunun .NET temsilcisine açıkça derlenmesini sağlayan derlenmiş sorguları destekler. Bu temsilci alındıktan sonra, LINQ ifade ağacını sağlamadan sorguyu yürütmek için doğrudan çağrılabilir. Bu teknik, önbellek aramasını atlar ve EF Core'da sorgu yürütmenin en iyi duruma getirilmiş yolunu sağlar. Derlenmiş ve derlenmemiş sorgu performansını karşılaştıran bazı karşılaştırma sonuçları aşağıdadır; herhangi bir karar vermeden önce platformunuzda karşılaştırma yapın. Kaynak kodu burada bulabilirsiniz, kendi ölçümleriniz için temel olarak kullanmaktan çekinmeyin.
Metot | NumBlogs | Ortalama | Hata | StdDev | 0. Nesil | Tahsis edilen |
---|---|---|---|---|---|---|
WithCompiledQuery | 1 | 564.2 bize | 6.75 bize | 5.99 bize | 1.9531 | 9 KB |
WithoutCompiledQuery | 1 | 671.6 biz | 12.72 biz | 16.54 bize | 2.9297 | 13 KB |
WithCompiledQuery | 10 | 645.3 bize | 10.00 bize | 9.35 bize | 2.9297 | 13 KB |
WithoutCompiledQuery | 10 | 709.8 biz | 25.20 bize | 73.10 bize | 3.9063 | 18 KB |
Derlenmiş sorguları kullanmak için önce aşağıdakiyle EF.CompileAsyncQuery bir sorgu derleyin (zaman uyumlu sorgular için kullanın EF.CompileQuery ):
private static readonly Func<BloggingContext, int, IAsyncEnumerable<Blog>> _compiledQuery
= EF.CompileAsyncQuery(
(BloggingContext context, int length) => context.Blogs.Where(b => b.Url.StartsWith("http://") && b.Url.Length == length));
Bu kod örneğinde EF'e bir örneği kabul eden bir DbContext
lambda ve sorguya geçirilecek rastgele bir parametre sunuyoruz. Artık sorguyu yürütmek istediğinizde bu temsilciyi çağırabilirsiniz:
await foreach (var blog in _compiledQuery(context, 8))
{
// Do something with the results
}
Temsilcinin iş parçacığı açısından güvenli olduğunu ve farklı bağlam örneklerinde eşzamanlı olarak çağrılabileceğini unutmayın.
Sınırlamalar
- Derlenmiş sorgular yalnızca tek bir EF Core modelde kullanılabilir. Aynı türdeki farklı bağlam örnekleri bazen farklı modeller kullanacak şekilde yapılandırılabilir; bu senaryoda derlenmiş sorguların çalıştırılması desteklenmez.
- Derlenmiş sorgularda parametreleri kullanırken basit, skaler parametreler kullanın. Örneklerde üye/yöntem erişimi gibi daha karmaşık parametre ifadeleri desteklenmez.
Sorgu önbelleğe alma ve parametreleştirme
EF yürütme için bir LINQ sorgu ağacı aldığında, önce bu ağacı "derlemeli", örneğin ondan SQL üretmelidir. Bu görev ağır bir işlem olduğundan, EF sorguları sorgu ağacı şekline göre önbelleğe alır, böylece aynı yapıya sahip sorgular dahili olarak önbelleğe alınmış derleme çıkışlarını yeniden kullanır. Bu önbelleğe alma, parametre değerleri farklı olsa bile aynı LINQ sorgusunu birden çok kez yürütmenin çok hızlı olmasını sağlar.
Aşağıdaki iki sorguyu göz önünde bulundurun:
var post1 = await context.Posts.FirstOrDefaultAsync(p => p.Title == "post1");
var post2 = await context.Posts.FirstOrDefaultAsync(p => p.Title == "post2");
İfade ağaçları farklı sabitler içerdiğinden, ifade ağacı farklılık gösterir ve bu sorguların her biri EF Core tarafından ayrı ayrı derlenir. Buna ek olarak, her sorgu biraz farklı bir SQL komutu üretir:
SELECT TOP(1) [b].[Id], [b].[Name]
FROM [Posts] AS [b]
WHERE [b].[Name] = N'post1'
SELECT TOP(1) [b].[Id], [b].[Name]
FROM [Posts] AS [b]
WHERE [b].[Name] = N'post2'
SQL farklı olduğundan veritabanı sunucunuzun aynı planı yeniden kullanmak yerine her iki sorgu için de bir sorgu planı oluşturması gerekebilir.
Sorgularınızda küçük bir değişiklik yapılması işleri önemli ölçüde değiştirebilir:
var postTitle = "post1";
var post1 = await context.Posts.FirstOrDefaultAsync(p => p.Title == postTitle);
postTitle = "post2";
var post2 = await context.Posts.FirstOrDefaultAsync(p => p.Title == postTitle);
Blog adı artık parametreli hale getirildiğinden, her iki sorgu da aynı ağaç şekline sahiptir ve EF'nin yalnızca bir kez derlenmesi gerekir. Üretilen SQL de parametrelendirilerek veritabanının aynı sorgu planını yeniden kullanmasına olanak sağlar:
SELECT TOP(1) [b].[Id], [b].[Name]
FROM [Posts] AS [b]
WHERE [b].[Name] = @__postTitle_0
Her sorguyu parametreleştirmeye gerek olmadığını unutmayın: sabitleri olan bazı sorguların olması son derece uygundur ve aslında veritabanları (ve EF) bazen sorgu parametrelendirildiğinde mümkün olmayan sabitler etrafında belirli iyileştirmeler gerçekleştirebilir. Düzgün parametreleştirmenin kritik öneme sahip olduğu bir örnek için dinamik olarak derlenmiş sorgular bölümüne bakın.
Not
EF Core'un ölçümleri Sorgu Önbelleği İsabet Oranını bildirir. Normal bir uygulamada, çoğu sorgu en az bir kez yürütüldükten sonra bu ölçüm program başlangıcından hemen sonra %100'e ulaşır. Bu ölçüm %100'ün altında kararlı kalırsa bu, uygulamanızın sorgu önbelleğini yenen bir şey yapıyor olabileceğinin bir göstergesidir. Bunu araştırmak iyi bir fikirdir.
Not
Veritabanının önbellek sorgu planlarını yönetme şekli veritabanına bağlıdır. Örneğin, SQL Server örtük olarak bir LRU sorgu planı önbelleği tutarken PostgreSQL bunu yapmaz (ancak hazırlanan deyimler çok benzer bir son etki oluşturabilir). Daha fazla ayrıntı için veritabanı belgelerinize bakın.
Dinamik olarak derlenmiş sorgular
Bazı durumlarda, LINQ sorgularını kaynak kodda tam olarak belirtmek yerine dinamik olarak oluşturmak gerekir. Örneğin, açık uçlu sorgu işleçleriyle (sıralama, filtreleme, sayfalama...) istemciden rastgele sorgu ayrıntıları alan bir web sitesinde bu durum oluşabilir. Prensipte, doğru yapılırsa, dinamik olarak yapılandırılmış sorgular normal sorgular kadar verimli olabilir (ancak derlenmiş sorgu iyileştirmesini dinamik sorgularla kullanmak mümkün değildir). Ancak uygulamada, genellikle performans sorunlarının kaynağıdır, çünkü her seferinde farklı şekillere sahip ifade ağaçlarını yanlışlıkla üretmek kolaydır.
Aşağıdaki örnekte, sorgunun Where
lambda ifadesini oluşturmak için üç teknik kullanılır:
- Sabit olan ifade API'si: İfade API'siyle ifadeyi dinamik olarak, sabit bir düğüm kullanarak oluşturun. Bu, ifade ağaçlarını dinamik olarak oluştururken sık yapılan bir hatadır ve EF'nin farklı bir sabit değerle her çağrıldığında sorguyu yeniden derlemesine neden olur (genellikle veritabanı sunucusunda plan önbelleği kirliliğine de neden olur).
- Parametresi olan ifade API'si: Sabiti bir parametreyle değiştiren daha iyi bir sürüm. Bu, sorgunun sağlanan değerden bağımsız olarak yalnızca bir kez derlendiğinden ve aynı (parametreli) SQL'in oluşturulmasından emin olur.
- Parametresiyle basit: Karşılaştırma için İfade API'sini kullanmayan ve yukarıdaki yöntemle aynı ağacı oluşturan ancak çok daha basit olan bir sürüm. Çoğu durumda İfade API'sine başvurmadan ifade ağacınızı dinamik olarak oluşturmak mümkündür ve bu da kolayca yanlış anlaşılır.
Sorguya yalnızca verilen parametre null değilse bir Where
işleç ekleriz. Bunun bir sorguyu dinamik olarak oluşturmak için iyi bir kullanım örneği olmadığını, ancak basitlik için kullandığımızı unutmayın:
[Benchmark]
public async Task<int> ExpressionApiWithConstant()
{
var url = "blog" + Interlocked.Increment(ref _blogNumber);
using var context = new BloggingContext();
IQueryable<Blog> query = context.Blogs;
if (_addWhereClause)
{
var blogParam = Expression.Parameter(typeof(Blog), "b");
var whereLambda = Expression.Lambda<Func<Blog, bool>>(
Expression.Equal(
Expression.MakeMemberAccess(
blogParam,
typeof(Blog).GetMember(nameof(Blog.Url)).Single()),
Expression.Constant(url)),
blogParam);
query = query.Where(whereLambda);
}
return await query.CountAsync();
}
Bu iki tekniğin karşılaştırması aşağıdaki sonuçları verir:
Metot | Ortalama | Hata | StdDev | 0. Nesil | 1. Nesil | Tahsis edilen |
---|---|---|---|---|---|---|
ExpressionApiWithConstant | 1,665,8 bize | 56.99 bize | 163.5 biz | 15.6250 | - | 109,92 KB |
ExpressionApiWithParameter | 757.1 biz | 35.14 bize | 103.6 bize | 12.6953 | 0.9766 | 54,95 KB |
SimpleWithParameter | 760.3 bize | 37.99 bize | 112.0 bize | 12.6953 | - | 55,03 KB |
Milisaniyenin altındaki fark küçük görünse bile, sabit sürümün önbelleği sürekli olarak kirlettiğine ve diğer sorguların yeniden derlenmesine neden olduğunu, bunları yavaşlattığını ve genel performansınızı genel olarak olumsuz etkilediğini unutmayın. Sabit sorgu yeniden derlemesini önlemek kesinlikle önerilir.
Not
Gerçekten gerekmedikçe ifade ağacı API'si ile sorgu oluşturmaktan kaçının. API'nin karmaşıklığının yanı sıra, bunları kullanırken yanlışlıkla önemli performans sorunlarına neden olmak çok kolaydır.
Derlenmiş modeller
Derlenmiş modeller, büyük modellere sahip uygulamalar için EF Core başlangıç süresini iyileştirebilir. Büyük bir model genellikle yüz binlerce varlık türü ve ilişkisi anlamına gelir. Burada başlangıç zamanı, uygulamada ilk kez bu tür kullanıldığında ilk işlemi DbContext
DbContext
gerçekleştirme zamanıdır. Yalnızca örnek DbContext
oluşturmanın EF modelinin başlatılmasına neden olmadığını unutmayın. Bunun yerine, modelin başlatılmasına neden olan tipik ilk işlemler ilk sorguyu çağırmak DbContext.Add
veya yürütmektir.
Derlenen modeller komut satırı aracı kullanılarak dotnet ef
oluşturulur.
Devam etmeden önce aracın en son sürümünü yüklediğinizden emin olun.
Derlenmiş modeli oluşturmak için yeni dbcontext optimize
bir komut kullanılır. Örneğin:
dotnet ef dbcontext optimize
--output-dir
ve --namespace
seçenekleri, derlenen modelin oluşturulacağı dizini ve ad alanını belirtmek için kullanılabilir. Örneğin:
PS C:\dotnet\efdocs\samples\core\Miscellaneous\CompiledModels> dotnet ef dbcontext optimize --output-dir MyCompiledModels --namespace MyCompiledModels
Build started...
Build succeeded.
Successfully generated a compiled model, to use it call 'options.UseModel(MyCompiledModels.BlogsContextModel.Instance)'. Run this command again when the model is modified.
PS C:\dotnet\efdocs\samples\core\Miscellaneous\CompiledModels>
- Daha fazla bilgi için bkz.
dotnet ef dbcontext optimize
. - Visual Studio'da daha rahat çalışıyorsanız Optimize-DbContext özelliğini de kullanabilirsiniz
Bu komutun çalıştırılmasından elde edilen çıkış, EF Core'un derlenmiş modeli kullanmasına neden olmak için yapılandırmanıza DbContext
kopyalayıp yapıştıracak bir kod parçası içerir. Örneğin:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseModel(MyCompiledModels.BlogsContextModel.Instance)
.UseSqlite(@"Data Source=test.db");
Derlenmiş model önyüklemesi
Genellikle oluşturulan önyükleme koduna bakmak gerekmez. Ancak bazen modeli veya yüklemesini özelleştirmek yararlı olabilir. Bootstrapping kodu şuna benzer:
[DbContext(typeof(BlogsContext))]
partial class BlogsContextModel : RuntimeModel
{
private static BlogsContextModel _instance;
public static IModel Instance
{
get
{
if (_instance == null)
{
_instance = new BlogsContextModel();
_instance.Initialize();
_instance.Customize();
}
return _instance;
}
}
partial void Initialize();
partial void Customize();
}
Bu, modeli gerektiği gibi özelleştirmek için uygulanabilen kısmi yöntemlere sahip kısmi bir sınıftır.
Ayrıca, bazı çalışma zamanı yapılandırmasına bağlı olarak farklı modeller kullanabilecek türler için DbContext
birden çok derlenmiş model oluşturulabilir. Bunlar, yukarıda gösterildiği gibi farklı klasörlere ve ad alanlarına yerleştirilmelidir. Daha sonra bağlantı dizesi gibi çalışma zamanı bilgileri incelenebilir ve gerektiğinde doğru model döndürülebilir. Örneğin:
public static class RuntimeModelCache
{
private static readonly ConcurrentDictionary<string, IModel> _runtimeModels
= new();
public static IModel GetOrCreateModel(string connectionString)
=> _runtimeModels.GetOrAdd(
connectionString, cs =>
{
if (cs.Contains("X"))
{
return BlogsContextModel1.Instance;
}
if (cs.Contains("Y"))
{
return BlogsContextModel2.Instance;
}
throw new InvalidOperationException("No appropriate compiled model found.");
});
}
Sınırlamalar
Derlenen modellerin bazı sınırlamaları vardır:
- Genel sorgu filtreleri desteklenmez.
- Gecikmeli yükleme ve değişiklik izleme proxy'leri desteklenmez.
- Model tanımı veya yapılandırma değiştiğinde model el ile yeniden oluşturularak eşitlenmelidir.
- Özel IModelCacheKeyFactory uygulamaları desteklenmez. Ancak, birden çok modeli derleyebilir ve gerektiği gibi uygun olanı yükleyebilirsiniz.
Bu sınırlamalar nedeniyle, derlenmiş modelleri yalnızca EF Core başlangıç süreniz çok yavaşsa kullanmanız gerekir. Küçük modelleri derlemek genellikle buna değmez.
Bu özelliklerden herhangi birinin desteklenmesi başarınız için kritik önem taşıyorsa lütfen yukarıda belirtilen uygun sorunlar için oy verin.
Çalışma zamanı ek yükünü azaltma
Her katmanda olduğu gibi EF Core da doğrudan alt düzey veritabanı API'lerine karşı kodlamaya kıyasla biraz çalışma zamanı yükü ekler. Bu çalışma zamanı ek yükünün gerçek dünya uygulamalarının çoğunu önemli ölçüde etkileme olasılığı düşüktür; bu performans kılavuzundaki sorgu verimliliği, dizin kullanımı ve gidiş dönüşleri en aza indirme gibi diğer konular çok daha önemlidir. Buna ek olarak, yüksek oranda iyileştirilmiş uygulamalar için bile ağ gecikme süresi ve veritabanı G/Ç genellikle EF Core'un içinde harcanan her zamana hakim olur. Ancak, her performans bitinin önemli olduğu yüksek performanslı, düşük gecikme süreli uygulamalarda EF Core ek yükünü en aza indirgemek için aşağıdaki öneriler kullanılabilir:
-
DbContext havuzunu açın; karşılaştırmalarımız bu özelliğin yüksek performans ve düşük gecikme süresine sahip uygulamalar üzerinde belirleyici bir etkiye sahip olabileceğini gösteriyor.
- öğesinin
maxPoolSize
kullanım senaryonuza karşılık olduğundan emin olun; çok düşükse örneklerDbContext
sürekli oluşturulur ve atılır ve performansı düşürür. Çok yüksek olarak ayarlamak, kullanılmayanDbContext
örnekler havuzda tutuldukçe gereksiz bir şekilde bellek tüketebilir. - Ek küçük performans artışı için DOĞRUDAN DI ekleme bağlam örneklerini kullanmak yerine kullanmayı
PooledDbContextFactory
göz önünde bulundurun. Havuz oluşturmanınDbContext
DI yönetimi hafif bir yüke neden olabilir.
- öğesinin
- Sık erişimli sorgular için önceden derlenmiş sorgular kullanın.
- LINQ sorgusu ne kadar karmaşıksa ve ne kadar çok işleç içeriyorsa ve sonuçta elde edilen ifade ağacı o kadar büyükse, derlenmiş sorguların kullanılmasından o kadar fazla kazanç beklenebilir.
- Bağlam yapılandırmanızda false olarak ayarlayarak
EnableThreadSafetyChecks
iş parçacığı güvenliği denetimlerini devre dışı bırakmayı göz önünde bulundurun.- Aynı
DbContext
örneğin farklı iş parçacıklarından eşzamanlı olarak kullanılması desteklenmez. EF Core' un çoğu durumda (tümü değil) bu programlama hatalarını algılayan ve hemen bilgilendirici bir özel durum oluşturan bir güvenlik özelliği vardır. Ancak, bu güvenlik özelliği çalışma zamanı ek yükü ekler. - UYARI: Yalnızca uygulamanızın bu tür eşzamanlılık hataları içermediğini kapsamlı bir şekilde test ettikten sonra iş parçacığı güvenliği denetimlerini devre dışı bırakın.
- Aynı