Hatékony lekérdezés

A hatékony lekérdezés egy hatalmas témakör, amely olyan széles körű témákat fed le, mint az indexek, a kapcsolódó entitásbetöltési stratégiák és sok más. Ez a szakasz néhány gyakori témát ismertet a lekérdezések gyorsabbá tételéhez, valamint a felhasználók által általában tapasztalt buktatókat.

Indexek megfelelő használata

A lekérdezések gyors futtatásának fő tényezője az, hogy szükség esetén megfelelően használja-e az indexeket: az adatbázisokat általában nagy mennyiségű adat tárolására használják, és a teljes táblákat érintő lekérdezések általában súlyos teljesítményproblémák forrásai. Az indexelési problémákat nem könnyű észrevenni, mert nem egyértelmű azonnal, hogy egy adott lekérdezés használ-e indexet. Például:

// Matches on start, so uses an index (on SQL Server)
var posts1 = await context.Posts.Where(p => p.Title.StartsWith("A")).ToListAsync();
// Matches on end, so does not use the index
var posts2 = await context.Posts.Where(p => p.Title.EndsWith("A")).ToListAsync();

Az indexelési problémák kiszúrásának jó módja, ha először egy lassú lekérdezést rögzít, majd megvizsgálja annak lekérdezési tervét az adatbázis kedvenc eszközén keresztül; erről további információt a teljesítménydiagnosztikával kapcsolatos oldalon talál. A lekérdezési terv megjeleníti, hogy a lekérdezés áthalad-e a teljes táblán, vagy indexet használ-e.

Általános szabály, hogy nincs speciális EF-tudás az indexek használatához vagy a hozzájuk kapcsolódó teljesítményproblémák diagnosztizálásához; az indexekkel kapcsolatos általános adatbázis-ismeretek ugyanolyan fontosak az EF-alkalmazásokhoz, mint az EF-t nem használó alkalmazásokhoz. Az alábbiakban felsorolunk néhány általános útmutatást, amelyek az indexek használatakor szem előtt tartandók:

  • Bár az indexek felgyorsítják a lekérdezéseket, a frissítéseket is lelassítják, mivel up-todátumot kell tartaniuk. Kerülje a nem szükséges indexek definiálását, és fontolja meg az indexszűrők használatát, hogy az indexet a sorok egy részhalmazára korlátozza, ezáltal csökkentve ezzel a többletterhelést.
  • Az összetett indexek felgyorsíthatják a több oszlopra szűrt lekérdezéseket, de felgyorsíthatják azokat a lekérdezéseket is, amelyek nem szűrnek az index összes oszlopára – a sorrendtől függően. Az A és a B oszlop indexe például felgyorsítja az A és B szerint szűrt lekérdezéseket, valamint a csak A által szűrt lekérdezéseket, de nem felgyorsítja a lekérdezések csak a B-vel való szűrést.
  • Ha egy lekérdezés egy oszlopon (például price / 2) egy kifejezés alapján szűr, akkor egyszerű index nem használható. Azonban definiálhat egy tárolt és rögzített oszlopot a kifejezéshez, és létrehozhat rajta indexet. Egyes adatbázisok a kifejezésindexeket is támogatják, amelyek közvetlenül felhasználhatók a lekérdezések bármely kifejezés általi szűrésének felgyorsítására.
  • A különböző adatbázisok lehetővé teszik az indexek különböző módon történő konfigurálását, és az EF Core-szolgáltatók sok esetben ezeket a Fluent API-n keresztül teszik elérhetővé. Az SQL Server-szolgáltató például lehetővé teszi annak konfigurálását, hogy egy index fürtözött-e, vagy megadhatja a kitöltési tényezőt. További információért tekintse meg a szolgáltató dokumentációját.

Csak a projekthez szükséges tulajdonságok

Az EF Core megkönnyíti az entitáspéldányok lekérdezését, majd a példányok kódban való használatát. Az entitáspéldányok lekérdezése azonban gyakran a szükségesnél több adatot vonhat vissza az adatbázisból. Vegye figyelembe a következőket:

await foreach (var blog in context.Blogs.AsAsyncEnumerable())
{
    Console.WriteLine("Blog: " + blog.Url);
}

Bár ehhez a kódhoz valójában csak az egyes blogtulajdonságok Url szükségesek, a teljes Blog entitás le lesz kérve, és a szükségtelen oszlopok átkerülnek az adatbázisból:

SELECT [b].[BlogId], [b].[CreationDate], [b].[Name], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]

Ezt úgy lehet optimalizálni Select , hogy megadjuk az EF-nek, mely oszlopokat kell kivetítenie.

await foreach (var blogName in context.Blogs.Select(b => b.Url).AsAsyncEnumerable())
{
    Console.WriteLine("Blog: " + blogName);
}

Az eredményként kapott SQL csak a szükséges oszlopokat húzza vissza:

SELECT [b].[Url]
FROM [Blogs] AS [b]

Ha több oszlopot ki kell vetítenie, vetítse ki a kívánt tulajdonságokat tartalmazó C# névtelen típusba.

Ügyeljen arra, hogy ez a technika nagyon hasznos a csak olvasható lekérdezésekhez, de mivel az EF változáskövetése csak entitáspéldányokkal működik, a dolgok bonyolultabbá válnak, ha frissíteni kell a lekért blogokat. Teljes entitások betöltése nélkül is végrehajthat frissítéseket, ha csatol egy módosított blogpéldányt, és közli az EF-fel, hogy mely tulajdonságok változtak meg; azonban ez egy fejlettebb technika, amely talán nem éri meg a ráfordítást.

Az eredményhalmaz méretének korlátozása

A lekérdezés alapértelmezés szerint az összes olyan sort adja vissza, amely megfelel a szűrőinek:

var blogsAll = await context.Posts
    .Where(p => p.Title.StartsWith("A"))
    .ToListAsync();

Mivel a visszaadott sorok száma az adatbázis tényleges adataitól függ, nem lehet tudni, hogy mennyi adat lesz betöltve az adatbázisból, mennyi memóriát vesznek fel az eredmények, és mennyi további terhelés jön létre az eredmények feldolgozásakor (például a hálózaton keresztüli felhasználói böngészőbe való küldéssel). Döntő fontosságú, hogy a tesztadatbázisok gyakran kevés adatot tartalmaznak, így a tesztelés során minden jól működik, de a teljesítményproblémák hirtelen megjelennek, amikor a lekérdezés valós adatokon kezd futni, és sok sor jelenik meg.

Ennek eredményeképpen általában érdemes megfontolni az eredmények számának korlátozását:

var blogs25 = await context.Posts
    .Where(p => p.Title.StartsWith("A"))
    .Take(25)
    .ToListAsync();

A felhasználói felület legalább egy olyan üzenetet jeleníthet meg, amely azt jelzi, hogy több sor is létezhet az adatbázisban (és lehetővé teheti az adatok más módon történő lekérését). A teljes körű megoldás a lapozást valósítja meg, ahol a felhasználói felület egyszerre csak bizonyos számú sort jelenít meg, és lehetővé teszi a felhasználók számára, hogy szükség szerint továbblépjenek a következő oldalra; A hatékony implementálásról a következő szakaszban talál további részleteket.

Hatékony lapozás

A lapozás az eredmények lapokban való lekérésére utal, nem pedig egyszerre; ez általában nagy eredményhalmazok esetében történik, ahol megjelenik egy felhasználói felület, amely lehetővé teszi a felhasználó számára az eredmények következő vagy előző oldalára való navigálást. Az adatbázisokkal való lapozás implementálásának gyakori módja a Skip és Take operátorok használata (OFFSET és LIMIT az SQL-ben); bár ez egy intuitív megoldás, ez meglehetősen kevésbé hatékony. A lapozáshoz, amely lehetővé teszi egy oldal egyidejű áthelyezését (nem pedig tetszőleges oldalakra való ugrást), fontolja meg a billentyűkészlet lapozását .

További információt a lapozás dokumentációs oldalán talál.

A relációs adatbázisokban az összes kapcsolódó entitás betöltődik a JOIN-ok egyetlen lekérdezésben való bevezetésével.

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]

Ha egy tipikus blog több kapcsolódó bejegyzéssel is rendelkezik, az ilyen bejegyzések sorai duplikálják a blog adatait. Ez a duplikáció az úgynevezett "cartesian robbanás" problémához vezet. A több egy-a-többhöz kapcsolat betöltésekor a duplikált adatok mennyiségének növekedése az alkalmazás teljesítményének romlásához vezethet.

Az EF lehetővé teszi ennek a hatásnak a elkerülését az "osztott lekérdezések" használatával, amelyek külön lekérdezésekkel töltik be a kapcsolódó entitásokat. További információkért olvassa el az osztott és az önálló lekérdezésekkel kapcsolatos dokumentációt.

Megjegyzés:

Az osztott lekérdezések jelenlegi implementációja minden lekérdezéshez egy körutat hajt végre. Azt tervezzük, hogy ezt a jövőben javítjuk, és az összes lekérdezést egyetlen körutazásban hajtjuk végre.

Javasoljuk, hogy a szakasz folytatása előtt olvassa el a kapcsolódó entitások dedikált lapját .

A kapcsolódó entitások kezelésekor általában előre tudjuk, mit kell betöltenünk: tipikus példa a blogok egy bizonyos készletének betöltése, valamint az összes bejegyzésük. Ezekben a forgatókönyvekben mindig jobb, ha eager loading-t használ, hogy az EF az összes szükséges adatot egyetlen körben le tudja kérni. A szűrt betöltés funkció lehetővé teszi, hogy korlátozza a betöltendő kapcsolódó entitásokat, miközben a betöltési folyamat előtöltött marad, és így egyetlen körutazás alatt hajtható végre.

using (var context = new BloggingContext())
{
    var filteredBlogs = await context.Blogs
        .Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToListAsync();
}

Más esetekben előfordulhat, hogy nem tudjuk, melyik kapcsolódó entitásra lesz szükségünk, mielőtt megkapnánk a fő entitást. Például egy blog betöltésekor előfordulhat, hogy más adatforrással – esetleg webszolgáltatással – kell foglalkoznunk, hogy megtudjuk, érdekel-e a blog bejegyzései. Ezekben az esetekben explicit vagy halasztott betöltéssel külön lekérheti a kapcsolódó entitásokat, és betöltheti a Blog bejegyzéseinek navigációját. Vegye figyelembe, hogy mivel ezek a módszerek nem hajlandók azonnal végrehajtani, további fordulókat igényelnek az adatbázishoz, ami lassulás forrása lehet; az adott forgatókönyvtől függően hatékonyabb lehet, ha mindig betölti az összes posztot, ahelyett, hogy végrehajtja a további fordulókat, és kizárólag a szükséges posztokat kapja meg.

Óvakodj a lusta betöltéstől

A lusta betöltés gyakran nagyon hasznos módszernek tűnik az adatbázislogika írásához, mivel az EF Core automatikusan betölti a kapcsolódó entitásokat az adatbázisból, amint a kód hozzáfér hozzájuk. Ez elkerüli a nem szükséges kapcsolódó entitások betöltését (például a explicit betöltést), és látszólag megszabadítja a programozót a kapcsolódó entitásokkal való teljes kezeléstől. A lusta betöltés azonban különösen hajlamos felesleges extra fordulók létrehozására, amelyek lelassíthatják az alkalmazást.

Vegye figyelembe a következőket:

foreach (var blog in await context.Blogs.ToListAsync())
{
    foreach (var post in blog.Posts)
    {
        Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
    }
}

Ez a látszólag ártatlan kódrészlet végigvezeti az összes blogon és a bejegyzéseiken, kinyomtatva őket. Az EF Core utasításnaplózásának bekapcsolása az alábbiakat mutatja be:

info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [b].[BlogId], [b].[Rating], [b].[Url]
      FROM [Blogs] AS [b]
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (5ms) [Parameters=[@__p_0='1'], CommandType='Text', CommandTimeout='30']
      SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
      FROM [Post] AS [p]
      WHERE [p].[BlogId] = @__p_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@__p_0='2'], CommandType='Text', CommandTimeout='30']
      SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
      FROM [Post] AS [p]
      WHERE [p].[BlogId] = @__p_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (1ms) [Parameters=[@__p_0='3'], CommandType='Text', CommandTimeout='30']
      SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
      FROM [Post] AS [p]
      WHERE [p].[BlogId] = @__p_0

... and so on

Mi folyik itt? Miért kerül sor ezek közül minden lekérdezésre a fent említett egyszerű hurkok esetében? A késleltetett betöltés során a blog bejegyzései csak akkor töltődnek be, amikor hozzáférünk a Bejegyzések tulajdonságához; ennek eredményeképpen minden az iteráción belüli foreach újabb adatbázis-lekérdezést indít el, mindegyik saját körével. Ennek eredményeképpen, miután a kezdeti lekérdezés betölti az összes blogot, blogonként egy másik lekérdezéssel rendelkezünk, betöltve az összes bejegyzést; ezt néha N+1-problémának is nevezik, és nagyon jelentős teljesítményproblémákat okozhat.

Feltételezve, hogy szükségünk lesz a blogok összes bejegyzésére, érdemes inkább a gyors betöltést használni. A betöltés végrehajtásához használhatjuk az Include operátort, de mivel csak a blogok URL-címére van szükségünk (és csak a szükséges adatokat kell betöltenünk). Ezért inkább egy kivetítőt használunk:

await foreach (var blog in context.Blogs.Select(b => new { b.Url, b.Posts }).AsAsyncEnumerable())
{
    foreach (var post in blog.Posts)
    {
        Console.WriteLine($"Blog {blog.Url}, Post: {post.Title}");
    }
}

Ezzel az EF Core egyetlen lekérdezésben lekéri az összes blogot – a bejegyzésekkel együtt . Bizonyos esetekben hasznos lehet a kombinatorikus robbanás hatásainak elkerülése osztott lekérdezések használatával.

Figyelmeztetés

Mivel a lusta betöltéssel rendkívül könnyű véletlenül aktiválni az N+1 problémát, ajánlott elkerülni. A korai vagy explicit betöltés során a forráskódban egyértelműen látható, amikor egy adatbázis-körút történik.

Pufferelés és streamelés

A pufferelés az összes lekérdezési eredmény memóriába való betöltését jelenti, míg a streamelés azt jelenti, hogy az EF minden alkalommal egyetlen eredményt ad át az alkalmazásnak, és soha nem tartalmazza a teljes eredményhalmazt a memóriában. A streamelési lekérdezések memóriakövetelményei elvben rögzítettek – megegyeznek attól, hogy a lekérdezés 1 sort vagy 1000-et ad vissza; A pufferelő lekérdezések azonban több memóriát igényelnek, minél több sort ad vissza a rendszer. A nagy eredményhalmazokat eredményező lekérdezések esetében ez fontos teljesítménytényező lehet.

Az, hogy egy lekérdezés pufferel-e vagy streamel, attól függ, hogyan van kiértékelve.

// ToList and ToArray cause the entire resultset to be buffered:
var blogsList = await context.Posts.Where(p => p.Title.StartsWith("A")).ToListAsync();
var blogsArray = await context.Posts.Where(p => p.Title.StartsWith("A")).ToArrayAsync();

// Foreach streams, processing one row at a time:
await foreach (var blog in context.Posts.Where(p => p.Title.StartsWith("A")).AsAsyncEnumerable())
{
    // ...
}

// AsAsyncEnumerable also streams, allowing you to execute LINQ operators on the client-side:
var doubleFilteredBlogs = context.Posts
    .Where(p => p.Title.StartsWith("A")) // Translated to SQL and executed in the database
    .AsAsyncEnumerable()
    .Where(p => SomeDotNetMethod(p)); // Executed at the client on all database results

Ha a lekérdezések csak néhány eredményt adnak vissza, akkor valószínűleg nem kell emiatt aggódnia. Ha azonban a lekérdezés nagy számú sort ad vissza, érdemes megfontolni a streamelést pufferelés helyett.

Megjegyzés:

Kerülje a használatot ToList , vagy ToArray ha egy másik LINQ-operátort szeretne használni az eredményen – ez szükségtelenül puffereli az összes eredményt a memóriába. A AsEnumerable használható helyette.

Belső pufferelés EF szerint

Bizonyos helyzetekben az EF maga is belsőleg puffereli az eredményhalmazt, függetlenül attól, hogy milyen módon értékeli ki a lekérdezést. A két eset, amikor ez történik, a következők:

  • Amikor egy újrapróbálkozási stratégiát alkalmaznak a végrehajtás során. Ez annak érdekében történik, hogy a rendszer ugyanazokat az eredményeket adja vissza, ha a lekérdezés később újrapróbálkozott.
  • Ha osztott lekérdezést használ, a rendszer az utolsó lekérdezés kivételével az összes eredményhalmazt puffereli , kivéve, ha a MARS (több aktív eredményhalmaz) engedélyezve van az SQL Serveren. Ennek az az oka, hogy általában lehetetlen egyszerre több lekérdezési eredményhalmazt aktiválni.

Vegye figyelembe, hogy a belső pufferelés az Ön által LINQ-operátorokon keresztül előidézett pufferelésen felül történik. Ha például egy lekérdezésnél használja a ToList elemet, és egy újrapróbálkozási végrehajtási stratégia van érvényben, az eredményhalmaz kétszer töltődik be a memóriába: egyszer belsőleg az EF, és egyszer a által.

Nyomon követés, nyomon követés nélküli és identitásfeloldás

Javasoljuk, hogy a szakasz folytatása előtt olvassa el a dedikált oldalt a nyomon követésről és a nyomkövetésről .

Az EF alapértelmezés szerint nyomon követi az entitáspéldányokat, így a rendszer a híváskor SaveChanges észleli és megőrzi a rajtuk lévő módosításokat. A lekérdezések nyomon követésének másik hatása, hogy az EF észleli, ha egy példány már betöltődött az adatokhoz, és automatikusan visszaadja a nyomon követett példányt, nem pedig egy újat. ezt identitásfeloldásnak nevezzük. Teljesítmény szempontjából a változáskövetés a következőket jelenti:

  • Az EF belsőleg karbantartja a nyomon követett példányok szótárát. Új adatok betöltésekor az EF ellenőrzi a szótárban, hogy az entitáskulcshoz már nyilvántartva van-e egy példány (azonosítás feloldása céljából). A szótár karbantartása és a keresések a lekérdezés eredményeinek betöltésekor némi időt vesz igénybe.
  • Mielőtt átad egy betöltött példányt az alkalmazásnak, az EF pillanatképeket hoz létre a példányról, és belsőleg tartja a pillanatképet. Amikor SaveChanges meghívják, az alkalmazás példánya össze lesz hasonlítva a pillanatképtel, hogy felderítse a megőrizendő módosításokat. A pillanatkép több memóriát vesz fel, és maga a pillanatképkészítési folyamat is időt vesz igénybe; néha lehetőség van eltérő, esetleg hatékonyabb pillanatképkészítési viselkedés megadására érték-összehasonlítókon keresztül, vagy változáskövetési proxyk használatával megkerülni a pillanatképkészítési folyamatot (bár ez a saját hátrányaiból áll).

Olyan írásvédett helyzetekben, ahol a módosítások nem kerülnek vissza az adatbázisba, a fent említett többletterhelések elkerülhetők nyomkövetés nélküli lekérdezések használatával. Mivel azonban a nyomkövetés nélküli lekérdezések nem hajtanak végre identitásfeloldási műveleteket, a rendszer különböző példányokként fog létrehozni egy adatbázissort, amelyre több más betöltött sor hivatkozik.

A szemléltetéshez tegyük fel, hogy nagy számú bejegyzést töltünk be az adatbázisból, valamint az egyes bejegyzések által hivatkozott blogot. Ha 100 bejegyzés ugyanarra a blogra hivatkozik, egy nyomkövetési lekérdezés identitásfeloldás segítségével észleli ezt, és minden bejegyzés példány ugyanarra a megkülönböztetett blogpéldányra hivatkozik. Ezzel szemben egy nyomkövetés nélküli lekérdezés 100-szor duplikálja ugyanazt a blogot , és az alkalmazáskódot ennek megfelelően kell írni.

Az alábbiakban egy olyan teljesítményteszt eredményeit mutatjuk be, amely összehasonlítja a nyomkövetést és a nem követési viselkedést egy 10 blogot és 20 bejegyzést betöltő lekérdezések esetében. A forráskód itt érhető el, nyugodtan használja saját mérései alapjául.

Metódus NumBlogs BejegyzésekSzámaBlogonként Jelent Hiba StdDev Medián Arány RatioSD Gen 0 Gen 1 Gen 2 Kiosztott
AsTracking 10 20 1,414,7 us 27.20 us 45.44 us 1 405,5 us 1,00 0.00 60,5469 13.6719 - 380.11 KB
AsNoTracking 10 20 993,3 mikroszekundum 24.04 us 65.40 us 966.2 us 0.71 0,05 37.1094 6.8359 - 232,89 KB

Végül a módosítások nyomon követésének többletterhelése nélkül is elvégezhetők frissítések, ha nem követési lekérdezést használnak, majd csatolják a visszaadott példányt a környezethez, meghatározva, hogy mely módosításokat kell végrehajtani. Ez átviszi a változáskövetés terhét az EF-ről a felhasználóra, és csak akkor szabad megkísérelni, ha a változáskövetési többletterhelés a profilkészítés vagy a teljesítményértékelés révén elfogadhatatlannak bizonyult.

SQL-lekérdezések használata

Bizonyos esetekben a lekérdezéshez optimalizáltabb SQL létezik, amelyet az EF nem hoz létre. Ez akkor fordulhat elő, ha az SQL-szerkezet az adatbázisra jellemző bővítmény, amely nem támogatott, vagy egyszerűen azért, mert az EF még nem fordítja le. Ezekben az esetekben az SQL kézzel történő írása jelentős teljesítménynövelést biztosíthat, és az EF számos módon támogatja ezt.

  • Sql-lekérdezések használata közvetlenül a lekérdezésben, például a FromSqlRaw. Az EF lehetővé teszi, hogy normál LINQ-lekérdezésekkel írja le az SQL-t, így a lekérdezésnek csak egy részét fejezheti ki az SQL-ben. Ez egy jó módszer, ha az SQL-t csak egyetlen lekérdezésben kell használni a kódbázisban.
  • Definiáljon egy felhasználó által definiált függvényt (UDF), majd hívja meg a lekérdezésekből. Vegye figyelembe, hogy az EF lehetővé teszi az UDF-k számára, hogy teljes eredményhalmazokat adjanak vissza – ezeket táblaértékű függvényeknek (TVF-eknek) nevezzük, és lehetővé teszi a DbSet függvények leképezését is, így ugyanúgy néz ki, mint egy másik tábla.
  • Definiáljon egy adatbázisnézetet és egy lekérdezést belőle a lekérdezésekben. Vegye figyelembe, hogy a függvényekkel ellentétben a nézetek nem fogadnak el paramétereket.

Megjegyzés:

A nyers SQL-t általában végső megoldásként kell használni, miután meggyőződett arról, hogy az EF nem tudja létrehozni a kívánt SQL-t, és ha a teljesítmény elég fontos ahhoz, hogy az adott lekérdezés igazolja azt. A nyers SQL használata jelentős karbantartási hátrányokat okoz.

Aszinkron programozás

Általános szabály, hogy ahhoz, hogy az alkalmazás méretezhető legyen, fontos, hogy mindig aszinkron API-kat használjon szinkron helyett (például SaveChangesAsync nem SaveChanges). A szinkron API-k blokkolják a szálat az adatbázis I/O-jának időtartama alatt, növelve a szálak szükségességét és a szálkörnyezeti kapcsolók számát.

További információkért tekintse meg az aszinkron programozásról szóló oldalt.

Figyelmeztetés

Kerülje a szinkron és aszinkron kód keverését ugyanabban az alkalmazásban – nagyon könnyű véletlenül finom szálkészlet-éhezési problémákat kiváltani.

Figyelmeztetés

A Microsoft.Data.SqlClient aszinkron implementációjának sajnos vannak ismert problémái (például #593, #601stb.). Ha váratlan teljesítménnyel kapcsolatos problémákat tapasztal, próbálkozzon inkább a szinkronizálási parancs végrehajtásával, különösen nagy szöveges vagy bináris értékek kezelésekor.

További erőforrások