Speciális teljesítménytémakörök
DbContext-készletezés
A DbContext
általában egy egyszerű objektum: a létrehozás és a törlés nem jár adatbázisművelettel, és a legtöbb alkalmazás ezt anélkül teheti meg, hogy észrevehető hatással lenne a teljesítményre. Azonban minden környezeti példány különböző belső szolgáltatásokat és objektumokat állít be a feladatai ellátásához, és a folyamatos működés többletterhelése jelentős lehet a nagy teljesítményű forgatókönyvekben. Ezekben az esetekben az EF Core készletet a környezeti példányokhoz: a környezet megsemmisítésekor az EF Core visszaállítja az állapotát, és belső készletben tárolja; amikor egy új példányt kérnek, a rendszer az új példány beállítása helyett a készletezett példányt adja vissza. A környezetkészletezés lehetővé teszi, hogy a környezetbeállítási költségeket csak egyszer fizesse meg a program indításakor, nem pedig folyamatosan.
Vegye figyelembe, hogy a környezetkészletezés ortogonális az adatbázis-kapcsolatkészletezéshez, amelyet az adatbázis-illesztőprogram alacsonyabb szintjén kezelnek.
Az EF Core-t használó ASP.NET Core-alkalmazások tipikus mintája egy egyéni DbContext típus regisztrálása a függőséginjektálási tárolóba AddDbContextkeresztül. Ezután a rendszer konstruktorparaméterekkel szerzi be az ilyen típusú példányokat a vezérlőkben vagy a Razor Pagesben.
A kontextus aggregáció engedélyezéséhez egyszerűen cserélje le a AddDbContext
-t AddDbContextPool-re.
builder.Services.AddDbContextPool<WeatherForecastContext>(
o => o.UseSqlServer(builder.Configuration.GetConnectionString("WeatherForecastContext")));
A poolSize
paramétere a AddDbContextPool által megtartott példányok maximális számát határozza meg (alapértelmezés szerint 1024). A poolSize
túllépése után a rendszer nem gyorsítótárazza az új környezeti példányokat, és az EF visszaesik az igény szerinti példányok létrehozásának nem készletezési viselkedésére.
Referenciaértékek
Az alábbiakban azokat a teljesítményteszt-eredményeket mutatjuk be, amelyek egyetlen sort kérnek le egy, ugyanazon a gépen helyileg futó SQL Server-adatbázisból környezetkészletezéssel és anélkül. Mint mindig, az eredmények a sorok számával, az adatbázis-kiszolgáló késésével és más tényezőkkel változnak. Fontos, hogy ez az egyszálas szálkiosztási teljesítményt méri, míg a valós erőforrás-kihasználtsági helyzetek eltérő eredményeket mutathatnak, ezért végezzen teljesítménytesztet a platformján, mielőtt bármilyen döntést hoz. A forráskód itt érhető el, nyugodtan használja saját mérései alapjául.
Módszer | NumBlogs | Jelent | Hiba | StdDev | Gen 0 | Gen 1 | Gen 2 | Kiosztott |
---|---|---|---|---|---|---|---|---|
WithoutContextPooling | 1 | 701.6 us | 26.62 us | 78.48 us | 11.7188 | - | - | 50,38 KB |
WithContextPooling | 1 | 350.1 us | 6.80 us | 14.64 us | 0.9766 | - | - | 4.63 KB |
Állapot menedzselése készletezett környezetekben
A környezetkészletezés úgy működik, hogy ugyanazt a környezeti példányt használja újra a kérelmek között; Ez azt jelenti, hogy hatékonyan regisztrálva van Singleton, és ugyanazt a példányt több kérelem (vagy DI-hatókör) között is újra felhasználja. Ez azt jelenti, hogy különös figyelmet kell fordítani arra az esetre, ha a környezet bármilyen olyan állapotot érint, amely a kérések között változhat. Döntő fontosságú, hogy a környezet OnConfiguring
csak egyszer hívható meg – a példány környezetének első létrehozásakor –, így nem használható a változó állapot (például bérlőazonosító) beállítására.
A környezetállapot tipikus forgatókönyve egy több-bérlős ASP.NET Core-alkalmazás, amelyben a környezeti példány egy bérlőazonosítóval rendelkezik, amelyet a lekérdezések figyelembe vesznek (további részletekért lásd globális lekérdezésszűrők). Mivel a bérlőazonosítónak minden webkérelemnél változnia kell, néhány további lépést kell tennünk, hogy minden működjön a kontextus megosztásával.
Tegyük fel, hogy az alkalmazás egy hatókörrel rendelkező ITenant
szolgáltatást regisztrál, amely a bérlőazonosítót és a bérlővel kapcsolatos egyéb információkat burkolja:
// 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;
});
A fentiekben leírtaknak megfelelően különös figyelmet kell fordítani arra, hogy honnan szerezze be a bérlőazonosítót – ez az alkalmazás biztonságának fontos eleme.
Miután rendelkezünk a hatókörű ITenant
szolgáltatással, regisztráljunk egy készletezési kontextusgyárat Singleton szolgáltatásként, a szokásos módon:
builder.Services.AddPooledDbContextFactory<WeatherForecastContext>(
o => o.UseSqlServer(builder.Configuration.GetConnectionString("WeatherForecastContext")));
Ezután írjon egy egyedi környezeti gyártót, amely a regisztrált Singleton-gyárból kapja a gyűjtött környezetet, és a bérlőazonosítót beilleszti a kiosztott környezetpéldányokba.
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;
}
}
Ha már rendelkezik az egyéni környezet-előállítóval, regisztrálja hatókörű szolgáltatásként:
builder.Services.AddScoped<WeatherForecastScopedFactory>();
Végül gondoskodjon egy környezetről, amely a Scoped-gyárból injektálható:
builder.Services.AddScoped(
sp => sp.GetRequiredService<WeatherForecastScopedFactory>().CreateDbContext());
Ezen a ponton a vezérlők automatikusan injektálnak egy olyan környezeti példányt, amely rendelkezik a megfelelő bérlőazonosítóval anélkül, hogy bármit is tudnia kellene róla.
A minta teljes forráskódja elérhető itt.
Jegyzet
Bár az EF Core gondoskodik az DbContext
és a kapcsolódó szolgáltatások belső állapotának alaphelyzetbe állításáról, általában nem állítja alaphelyzetbe az állapotot a mögöttes adatbázis-illesztőprogramban, amely kívül esik az EF-n. Például, ha manuálisan nyit meg és használ egy DbConnection
-t, vagy más módon módosítja az ADO.NET állapotát, akkor Ön felelős az állapot helyreállításáért, mielőtt a környezeti példányt visszaadná a készletbe, például a kapcsolat bezárásával. Ennek elmulasztása azt eredményezheti, hogy az állapot kiszivárog a nem kapcsolódó kérések között.
Kapcsolatkészletezési szempontok
A legtöbb adatbázis esetében hosszú élettartamú kapcsolat szükséges az adatbázis-műveletek végrehajtásához, és az ilyen kapcsolatok megnyitása és bezárása költséges lehet. Az EF nem implementálja magát a kapcsolatkészletezést, hanem az alapul szolgáló adatbázis-illesztőprogramra (például ADO.NET illesztőre) támaszkodik az adatbázis-kapcsolatok kezeléséhez. A kapcsolatkészletezés egy ügyféloldali mechanizmus, amely újra felhasználja a meglévő adatbázis-kapcsolatokat, hogy csökkentse a kapcsolatok ismételt megnyitásának és bezárásának többletterhelését. Ez a mechanizmus általánosan konzisztens az EF által támogatott adatbázisokban, például az Azure SQL Database-ben, a PostgreSQL-ben és más adatbázisokban, bár az adatbázisra vagy környezetre jellemző tényezők, például az erőforráskorlátok vagy a szolgáltatáskonfigurációk befolyásolhatják a készletezési hatékonyságot. A kapcsolatkészletezés alapértelmezés szerint engedélyezve van, és a készletezési konfigurációt az adott illesztőprogram által dokumentált alacsony szintű illesztőprogram szintjén kell végrehajtani; például ADO.NET használatakor a paraméterek, például a minimális vagy maximális készletméretek általában a kapcsolati sztringen keresztül vannak konfigurálva.
A kapcsolatkészletezés teljesen ortogonális az EF DbContext
készletezéséhez, amelyet fentebb ismertetünk: míg az alacsony szintű adatbázis-illesztőprogramok adatbázis-kapcsolatokat készleteznek (a nyitott/záró kapcsolatok többletterhelésének elkerülése érdekében), az EF képes a környezeti példányok készletezésére (a környezeti memóriafoglalás és az inicializálás többletterhelésének elkerülése érdekében). Függetlenül attól, hogy egy kontextuspéldány készletezett vagy sem, az EF általában közvetlenül minden művelet (pl. lekérdezés) előtt nyitja meg a kapcsolatokat, és közvetlenül utána zárja be azokat, így azok visszakerülnek a készletbe; ezzel elkerülhető, hogy a kapcsolatok a szükségesnél hosszabb ideig maradjanak a készleten kívül.
Lefordított lekérdezések
Amikor az EF kap egy LINQ-lekérdezési fát a végrehajtáshoz, először "lefordítania" kell azt a fát, például sql-t kell létrehoznia belőle. Mivel ez a feladat egy nehéz folyamat, az EF gyorsítótárazza a lekérdezéseket a lekérdezésfa alakzata alapján, így az azonos szerkezetű lekérdezések újra felhasználják a belső gyorsítótárazott fordítási kimeneteket. Ez a gyorsítótárazás biztosítja, hogy ugyanazon LINQ-lekérdezés többszöri végrehajtása nagyon gyors legyen, még akkor is, ha a paraméterértékek eltérnek.
Az EF-nek azonban továbbra is el kell végeznie bizonyos feladatokat, mielőtt használhatja a belső lekérdezési gyorsítótárat. A lekérdezés kifejezésfáját például rekurzívan össze kell hasonlítani a gyorsítótárazott lekérdezések kifejezésfáival a megfelelő gyorsítótárazott lekérdezés megtalálásához. Ennek a kezdeti feldolgozásnak a többlettere az EF-alkalmazások többségében elhanyagolható, különösen akkor, ha összehasonlítjuk a lekérdezések végrehajtásával kapcsolatos egyéb költségekkel (hálózati I/O, tényleges lekérdezésfeldolgozás és lemez I/O az adatbázisban...). Bizonyos nagy teljesítményű forgatókönyvekben azonban célszerű lehet megszüntetni.
Az EF támogatja lefordított lekérdezéseket, amelyek lehetővé teszik a LINQ-lekérdezések explicit fordítását .NET-meghatalmazottakká. A delegált beszerzése után közvetlenül meghívható a lekérdezés végrehajtásához a LINQ-kifejezésfa megadása nélkül. Ez a technika áthalad a gyorsítótár-keresésen, és a lekérdezések EF Core-ban való végrehajtásának legoptimalizáltabb módját biztosítja. Az alábbiakban néhány benchmark eredményt mutatunk be, amelyek a lefordított és a nem lefordított lekérdezések teljesítményét hasonlítják össze; végezzen teljesítménytesztet a platformján, mielőtt bármilyen döntést hozna. A forráskód itt érhető el, nyugodtan használja saját mérései alapjául.
Módszer | NumBlogs | Jelent | Hiba | StdDev | Gen 0 | Kiosztott |
---|---|---|---|---|---|---|
WithCompiledQuery | 1 | 564.2 us | 6.75 us | 5.99 us | 1.9531 | 9 KB |
Fordítás nélküli lekérdezés | 1 | 671.6 us | 12.72 us | 16.54 us | 2.9297 | 13 KB |
WithCompiledQuery | 10 | 645.3 us | 10.00 USA | 9.35 us | 2.9297 | 13 KB |
Összeállított Kérdés Nélkül | 10 | 709.8 us | 25.20 us | 73.10 us | 3,9063 | 18 KB |
Lefordított lekérdezések használatához először állítson össze egy lekérdezést EF.CompileAsyncQuery az alábbiak szerint (a szinkron lekérdezésekhez használja a 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));
Ebben a kódmintában egy DbContext
példányt elfogadó lambdát és egy tetszőleges paramétert adunk át a lekérdezésnek. Most már meghívhatja ezt a delegáltat, amikor végre szeretné hajtani a lekérdezést:
await foreach (var blog in _compiledQuery(context, 8))
{
// Do something with the results
}
Vegye figyelembe, hogy a delegált szálbiztos, és párhuzamosan meghívható különböző kontextus példányokon.
Korlátozások
- A lefordított lekérdezések csak egyetlen EF Core modell esetén használhatók. Az azonos típusú különböző környezeti példányok néha különböző modellek használatára konfigurálhatók; a lefordított lekérdezések futtatása ebben a forgatókönyvben nem támogatott.
- Ha paramétereket használ a lefordított lekérdezésekben, használjon egyszerű, skaláris paramétereket. Az összetettebb paraméterkifejezések – például a példányok tag-/metódushozzáférései – nem támogatottak.
Lekérdezések gyorsítótárazása és paraméterezése
Amikor az EF kap egy LINQ-lekérdezési fát a végrehajtáshoz, először "lefordítania" kell azt a fát, például sql-t kell létrehoznia belőle. Mivel ez a feladat egy nehéz folyamat, az EF gyorsítótárazza a lekérdezéseket a lekérdezésfa alakzata alapján, így az azonos szerkezetű lekérdezések újra felhasználják a belső gyorsítótárazott fordítási kimeneteket. Ez a gyorsítótárazás biztosítja, hogy ugyanazon LINQ-lekérdezés többszöri végrehajtása nagyon gyors legyen, még akkor is, ha a paraméterértékek eltérnek.
Fontolja meg a következő két lekérdezést:
var post1 = await context.Posts.FirstOrDefaultAsync(p => p.Title == "post1");
var post2 = await context.Posts.FirstOrDefaultAsync(p => p.Title == "post2");
Mivel a kifejezésfák különböző állandókat tartalmaznak, a kifejezésfa eltér, és ezeket a lekérdezéseket az EF Core külön állítja össze. Ezenkívül minden lekérdezés egy kissé eltérő SQL-parancsot hoz létre:
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'
Mivel az SQL eltér, az adatbázis-kiszolgálónak valószínűleg mindkét lekérdezéshez létre kell majd készítenie egy lekérdezési tervet ahelyett, hogy ugyanazt a tervet újrahasználná.
A lekérdezések kis mértékű módosítása jelentősen megváltoztathatja a dolgokat:
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);
Mivel a blog neve mostantól paraméteres, mindkét lekérdezésnek ugyanaz a faalakzata van, és az EF-t csak egyszer kell lefordítani. A létrehozott SQL is paraméteres, így az adatbázis újra felhasználhatja ugyanazt a lekérdezési tervet:
SELECT TOP(1) [b].[Id], [b].[Name]
FROM [Posts] AS [b]
WHERE [b].[Name] = @__postTitle_0
Vegye figyelembe, hogy nincs szükség minden egyes lekérdezés paraméterezésére: tökéletesen rendben van, ha egyes lekérdezések állandókkal rendelkeznek, és az adatbázisok (és az EF) néha bizonyos optimalizálást végezhetnek az állandók körül, amelyek nem lehetségesek a lekérdezés paraméterezésekor. Tekintse meg dinamikusan létrehozott lekérdezések című szakaszt, ahol a megfelelő paraméterezés kulcsfontosságú.
Jegyzet
Az EF Core metrikái a lekérdezési gyorsítótár találati arányát jelzik. Normál alkalmazásokban ez a metrika a program indítása után nem sokkal eléri a 100%, miután a legtöbb lekérdezés legalább egyszer végre lett hajtva. Ha ez a metrika 100%alatt marad, az azt jelzi, hogy az alkalmazás olyasmit csinál, amely legyőzi a lekérdezési gyorsítótárat – érdemes ezt megvizsgálni.
Jegyzet
Hogy az adatbázis hogyan kezeli a gyorsítótárak lekérdezési terveit, az adatbázis-függő. Az SQL Server például implicit módon tart fenn egy LRU lekérdezésterv-gyorsítótárat, míg a PostgreSQL nem (de az előkészített utasítások nagyon hasonló véghatást eredményezhetnek). További részletekért tekintse meg az adatbázis dokumentációját.
Dinamikusan létrehozott lekérdezések
Bizonyos helyzetekben a LINQ-lekérdezéseket dinamikusan kell összeállítani ahelyett, hogy egyenesen a forráskódban adhatja meg őket. Ez történhet például egy olyan webhelyen, amely tetszőleges lekérdezési adatokat fogad egy ügyféltől, nyílt végű lekérdezési operátorokkal (rendezés, szűrés, lapozás...). Ha helyesen történik, a dinamikusan létrehozott lekérdezések elvileg ugyanolyan hatékonyak lehetnek, mint a hagyományosak (bár a lefordított lekérdezésoptimalizálás dinamikus lekérdezésekkel nem használható). A gyakorlatban azonban gyakran ezek a teljesítményproblémák forrása, mivel könnyen lehet véletlenül olyan kifejezésfákat létrehozni, amelyek minden alkalommal eltérő alakzatokkal rendelkeznek.
Az alábbi példa három technikát használ egy lekérdezés Where
lambda kifejezésének létrehozásához:
- Expression API állandó: Dinamikusan hozza létre a kifejezést a Kifejezés API-val egy állandó csomópont használatával. Ez gyakori hiba, amikor dinamikusan épít ki kifejezésfákat, és az EF minden alkalommal újrafordítást okoz a lekérdezésben, amikor más állandó értékkel hívják meg (ez általában a terv gyorsítótárának szennyezését is okozza az adatbázis-kiszolgálón).
- Expression APIparaméterrel: Jobb verzió, amely az állandót egy paraméterrel helyettesíti. Ez biztosítja, hogy a lekérdezés csak egyszer legyen lefordítva a megadott értéktől függetlenül, és a rendszer ugyanazt a (paraméteres) SQL-t hozza létre.
- Egyszerű aparaméterrel: Olyan verzió, amely nem használja a Expression API-t összehasonlításra, amely ugyanazt a fát hozza létre, mint a fenti módszer, de sokkal egyszerűbb. Sok esetben dinamikusan építheti ki a kifejezésfát anélkül, hogy a Kifejezés API-t használja, ami könnyen tévedhető.
Csak akkor adunk hozzá Where
operátort a lekérdezéshez, ha a megadott paraméter nem null. Vegye figyelembe, hogy ez nem jó eset a lekérdezések dinamikus létrehozására , de az egyszerűség kedvéért használjuk:
[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();
}
A két módszer teljesítményértékelése a következő eredményeket adja:
Módszer | Jelent | Hiba | StdDev | Gen0 | Gen1 | Kiosztott |
---|---|---|---|---|---|---|
ExpressionApiWithConstant | 1665,8 us | 56.99 us | 163.5 us | 15.6250 | - | 109,92 KB |
ExpressionApiWithParameter | 757.1 us | 35.14 us | 103.6 us | 12,6953 | 0.9766 | 54,95 KB |
SimpleWithParameter | 760.3 us | 37.99 us | 112.0 us | 12,6953 | - | 55.03 KB |
Még akkor is, ha az ezredmásodperc alatti különbség kicsinek tűnik, ne feledje, hogy az állandó verzió folyamatosan szennyezi a gyorsítótárat, és más lekérdezéseket is újrafordít, lelassítja őket, és általános negatív hatással van a teljes teljesítményre. Erősen ajánlott elkerülni az állandó lekérdezések újrafordítását.
Jegyzet
Ne hozzon létre lekérdezéseket a kifejezésfa API-val, hacsak nem szükséges. Az API összetettségén kívül nagyon könnyű véletlenül jelentős teljesítményproblémákat okozni használatuk során.
Lefordított modellek
A lefordított modellek javíthatják az EF Core indítási idejét a nagy modellekkel rendelkező alkalmazások esetében. A nagy modell általában több száz–több ezer entitástípust és kapcsolatot jelent. Itt az indítási idő az DbContext
-en végzett első művelet ideje, amikor a DbContext
típust először használják az alkalmazásban. Vegye figyelembe, hogy csak egy DbContext
-példány létrehozása nem eredményezi az EF-modell inicializálását. Ehelyett a modell inicializálását okozó tipikus első műveletek közé tartozik a DbContext.Add
hívása vagy az első lekérdezés végrehajtása.
A lefordított modellek a dotnet ef
parancssori eszközzel jönnek létre. A folytatás előtt győződjön meg arról, hogy telepítette az eszköz legújabb verzióját.
A lefordított modell létrehozásához egy új dbcontext optimize
parancsot használunk. Például:
dotnet ef dbcontext optimize
A --output-dir
és --namespace
beállításokkal megadhatja azt a könyvtárat és névteret, amelybe a lefordított modell létrejön. Például:
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>
- További információ:
dotnet ef dbcontext optimize
. - Ha kényelmesebben dolgozik a Visual Studióban, akkor használhatja az Optimize-DbContext-t is.
A parancs futtatásának kimenete tartalmaz egy kódrészletet, amelyet a DbContext
konfigurációba másolhat és beilleszthet, így az EF Core használhatja a lefordított modellt. Például:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseModel(MyCompiledModels.BlogsContextModel.Instance)
.UseSqlite(@"Data Source=test.db");
Összeállított modell indítása
Általában nem szükséges megnézni a létrehozott rendszerindítási kódot. Néha azonban hasznos lehet a modell vagy annak betöltési folyamatának testreszabása. A rendszerindítási kód a következőképpen néz ki:
[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();
}
Ez egy részleges osztály részleges metódusokkal, amelyek a modell igény szerinti testreszabásához implementálhatók.
Emellett több lefordított modell is létrehozható DbContext
olyan típusokhoz, amelyek bizonyos futtatókörnyezeti konfigurációtól függően különböző modelleket használhatnak. Ezeket a fent látható módon különböző mappákba és névterekbe kell helyezni. Ezután megvizsgálhatja a futtatókörnyezet adatait, például a kapcsolati sztringet, és szükség szerint visszaadhatja a megfelelő modellt. Például:
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.");
});
}
Korlátozások
A lefordított modellek bizonyos korlátozásokkal rendelkeznek:
- globális lekérdezésszűrők nem támogatottak.
- A lusta betöltési és változáskövetési proxyk nem támogatottak.
- A modellt manuálisan kell szinkronizálni, ha a modell definíciójában vagy konfigurációjában változás történik, úgy hogy újra kell generálni.
- Az egyéni IModelCacheKeyFactory-implementációk nem támogatottak. Azonban több modellt is összeállíthat, és szükség szerint betöltheti a megfelelőt.
Ezen korlátozások miatt csak akkor érdemes lefordított modelleket használnia, ha az EF Core indítási ideje túl lassú. A kis modellek összeállítása általában nem éri meg.
Ha a fenti funkciók támogatása kritikus fontosságú a sikeresség szempontjából, szavazzon a fent hivatkozott megfelelő problémákra.
A futtatókörnyezet többletterhelésének csökkentése
Mint minden rétegnél, az EF Core is hozzáad egy kis futásidejű többletterhelést az alacsonyabb szintű adatbázis API-k közvetlen kódolásához képest. Ez a futtatókörnyezeti többletterhelés valószínűleg nem befolyásolja jelentős mértékben a legtöbb valós alkalmazást; A teljesítménymutató egyéb témakörei, például a lekérdezés hatékonysága, az indexhasználat és a kerekítések minimalizálása sokkal fontosabbak. Emellett még a nagy mértékben optimalizált alkalmazások esetében is a hálózati késés és az adatbázis I/O általában uralni fogja az EF Core-ban töltött időt. A nagy teljesítményű, alacsony késésű alkalmazások esetében azonban, ahol minden egyes perf fontos, az alábbi javaslatok segítségével minimálisra csökkenthető az EF Core többletterhelése:
- Kapcsolja be a DbContext-készletezést; a teljesítménymutatók azt mutatják, hogy ez a funkció döntő hatással lehet a nagy teljesítményű, alacsony késésű alkalmazásokra.
- Győződjön meg arról, hogy a
maxPoolSize
megfelel az Ön használati esetének; ha túl alacsony, aDbContext
példányok folyamatosan létrejönnek és megsemmisülnek, ami rontja a teljesítményt. Ha túl magasra állítja, feleslegesen használjon memóriát, mivel a nem használtDbContext
példányok megmaradnak a készletben. - Egy extra apró teljesítménynövekedés érdekében fontolja meg a
PooledDbContextFactory
használatát ahelyett, hogy a DI közvetlenül injektálná a környezeti példányokat. ADbContext
csoportosítás DI-kezelése némi többletköltséget okoz.
- Győződjön meg arról, hogy a
- Használjon előre összeállított lekérdezéseket gyakori lekérdezésekhez.
- Minél összetettebb a LINQ-lekérdezés – minél több operátort tartalmaz, és annál nagyobb az eredményül kapott kifejezésfa – annál több nyereség várható a lefordított lekérdezések használatából.
- Fontolja meg a szálbiztonsági ellenőrzések letiltását úgy, hogy
EnableThreadSafetyChecks
hamisra állítja a környezetkonfigurációban.- Ha ugyanazt a
DbContext
példányt egyidejűleg használja különböző szálakból, az nem támogatott. Az EF Core rendelkezik egy biztonsági funkcióval, amely sok esetben észleli ezt a programozási hibát (de nem az összeset), és azonnal egy informatív kivételt vet fel. Ez a biztonsági funkció azonban némi futásidejű többletterhelést is tartalmaz. - FIGYELMEZTETÉS: Csak akkor tiltsa le a szálbiztonsági ellenőrzéseket, ha alaposan tesztelte, hogy az alkalmazás nem tartalmaz-e ilyen egyidejűségi hibákat.
- Ha ugyanazt a