Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Používejte asynchronní metody založené na úkolech, abyste se vyhnuli blokování vlákna, zatímco Microsoft. Data.SqlClient čeká na připojení a síťové I/O. Asynchronní provádění nečiní příkaz ani objekt reader bezpečnými pro současné použití a nesnižuje dobu vykonávání dotazu na serveru.
Příklady dotazování aplikačních tabulek používají ukázkovou databázi AdventureWorks. Příklady v C# vyžadují using Microsoft.Data.SqlClient;.
Převést volací cestu na asynchronní kód
Převádějte jednu kompletní cestu hovoru najednou:
- Upravte obsahující metodu tak, aby byla asynchronní.
- Nahraďte připojení, příkazy, readery a transakční volání jejich úkolovými protějšky.
-
awaitpo každé operaci před opětovným použitím stejného objektu. - Předejte token zrušení volajícího každou operaci, kterou může volající zrušit.
- Udržujte omezený časový limit příkazu jako limit operace v databázi.
Použijte await od otevření připojení až po zpracování výsledků. Následující úryvek předpokládá, že connectionString a sql jsou již definovány. Vytvoří CancellationTokenSource s konečným časovým limitem pro operaci:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellation.Token);
using var command = new SqlCommand(sql, connection);
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellation.Token);
while (await reader.ReadAsync(cancellation.Token))
{
Console.WriteLine(reader.GetValue(0));
}
Metoda ExecuteReaderAsync vrací, jakmile je k dispozici první výsledek, ne až dorazí všechny řádky. Každé volání ReadAsync posune o jeden řádek vpřed, takže smyčka zpracovává řádky při jejich příchodu a uvolňuje vlákno pouze během čekání na další data ze serveru. 30sekundový token omezuje celou operaci, včetně smyčky čtení.
Pouze jedno asynchronní provedení může čekat na SqlCommand. Na jednom SqlDataReader může být nevyřízena pouze jedna operace: jedno asynchronní čtení, přechod výsledku nebo operace s polem. Čekejte na každou operaci před zahájením další operace na stejném objektu.
Asynchronní programovací funkce
Následující synchronní metody mají protějšky založené na úkolech, které přijímají token zrušení:
| Synchronní metoda | Asynchronní metoda | Příklad |
|---|---|---|
SqlConnection.Open |
SqlConnection.OpenAsync |
Převeďte synchronní metodu |
SqlCommand.ExecuteNonQuery |
SqlCommand.ExecuteNonQueryAsync |
Spouštějte příkazy asynchronně |
SqlCommand.ExecuteScalar |
SqlCommand.ExecuteScalarAsync |
Spouštějte příkazy asynchronně |
SqlCommand.ExecuteXmlReader |
SqlCommand.ExecuteXmlReaderAsync |
Spouštějte příkazy asynchronně |
SqlCommand.ExecuteReader |
SqlCommand.ExecuteReaderAsync |
Převést volací cestu na asynchronní kód |
SqlDataReader.Read |
SqlDataReader.ReadAsync |
Převést volací cestu na asynchronní kód |
SqlDataReader.NextResult |
SqlDataReader.NextResultAsync |
Načítejte výsledky asynchronně |
SqlDataReader.IsDBNull |
SqlDataReader.IsDBNullAsync |
Čtěte výsledky asynchronně |
SqlDataReader.GetFieldValue<T> |
SqlDataReader.GetFieldValueAsync<T> |
Číst výsledky asynchronně |
SqlBatch.ExecuteNonQuery, ExecuteScalar a ExecuteReader |
SqlBatch.ExecuteNonQueryAsync, ExecuteScalarAsync a ExecuteReaderAsync |
Spustit dávku |
SqlBulkCopy.WriteToServer |
SqlBulkCopy.WriteToServerAsync |
Používejte asynchronní operace se SqlBulkCopy |
Používejte synchronní uvolnění prostředků, když objekt implementuje pouze IDisposable. Použijte await using pro objekty, které implementují IAsyncDisposable. Čtečku odpojte před opětovným použitím připojení.
Spouštějte příkazy asynchronně
Následující příklady předpokládají, že je otevřená connection . Použijte ExecuteScalarAsync pro jednu hodnotu a předejte token pro zrušení vlastněný volajícím:
using var cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(30));
CancellationToken token = cancellation.Token;
using var count = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product;",
connection);
int total = (int)(await count.ExecuteScalarAsync(token))!;
Console.WriteLine($"Row count: {total}");
Použijte ExecuteNonQueryAsync pro příkazy měnící data. Použít ExecuteXmlReaderAsync pro FOR XML dotaz. Uvolněte objekt XML reader před vykonáním dalšího příkazu na stejném spojení. Bez více aktivních sad výsledků (MARS) umožňuje připojení mít otevřený pouze jeden objekt pro čtení dat.
Číst výsledky asynchronně
Následující příklad předává stejný token zrušení každému z těchto asynchronních hovorů:
-
ExecuteReaderAsyncspustí dotaz. -
ReadAsyncčte každý řádek. -
GetFieldValueAsyncčte hodnotu každého sloupce. -
NextResultAsyncpřechází do další množiny výsledků. -
IsDBNullAsynckontrolujeNULL.
using var cancellation = new CancellationTokenSource(
TimeSpan.FromSeconds(30));
CancellationToken token = cancellation.Token;
using var query = new SqlCommand("""
SELECT TOP (3) ProductID, Name
FROM Production.Product
ORDER BY ProductID;
SELECT TOP (1) ProductID, Color
FROM Production.Product
WHERE Color IS NULL;
""", connection);
await using SqlDataReader reader = await query.ExecuteReaderAsync(token);
while (await reader.ReadAsync(token))
{
int id = await reader.GetFieldValueAsync<int>(0, token);
string name = await reader.GetFieldValueAsync<string>(1, token);
Console.WriteLine($"{id}: {name}");
}
if (await reader.NextResultAsync(token) && await reader.ReadAsync(token))
{
int productId = await reader.GetFieldValueAsync<int>(0, token);
bool colorIsNull = await reader.IsDBNullAsync(1, token);
Console.WriteLine($"{productId}: Color is NULL: {colorIsNull}");
}
NextResultAsync přechází do další množiny výsledků.
GetFieldValueAsync a IsDBNullAsync přečtou jeden sloupec aktuálního řádku. Když použijete výchozí CommandBehavior, je řádek již uložen v bufferu, takže tato volání se obvykle dokončí bez čekání. Když použijete CommandBehavior.SequentialAccess, mohou tyto prvky čekat na data sloupců ze sítě. Pro více informací o sekvenčním přístupu viz podpora streamování SqlClient.
Převést synchronní metodu
Tato synchronní metoda otevře spojení, spustí příkaz a vrátí počet řádků v sys.objects:
static int CountObjects(string connectionString)
{
using var connection = new SqlConnection(connectionString);
connection.Open();
using var command = new SqlCommand("SELECT COUNT(*) FROM sys.objects;", connection);
return (int)command.ExecuteScalar()!;
}
Pro převod metody do asynchronního kódu proveďte tyto změny:
- Změňte typ návratu na
Task<int>. - Přidejte parametr
CancellationToken. - Nahraďte
OpenAsynczaExecuteScalarAsyncaOpenzaExecuteScalar. - Uvolněte připojení s
await using.
Převedená metoda vypadá takto:
static async Task<int> CountObjectsAsync(
string connectionString,
CancellationToken cancellationToken)
{
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
using var command = new SqlCommand("SELECT COUNT(*) FROM sys.objects;", connection);
return (int)(await command.ExecuteScalarAsync(cancellationToken))!;
}
Volající vytvoří token a čeká na výsledek:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
int objectCount = await CountObjectsAsync(connectionString, cancellation.Token);
Console.WriteLine($"Objects: {objectCount}");
Obě metody vracejí stejný počet. Výstup závisí na databázi, ke které se připojíte.
Connect Timeout Stále však omezuje navázání spojení a čekání na přidělení připojení z poolu. Zrušení tokenu může volajícímu vrátit kontrolu dříve.
Přidejte asynchronní volání do existující aplikace
Převádějte jednu kompletní cestu hovoru najednou. Vyhněte se volání .Result, .Wait() nebo GetAwaiter().GetResult() u databázových úloh, protože tato volání blokují a mohou vést k uváznutí v prostředích se synchronizačním kontextem.
Nespouštějte asynchronní příkaz a pak nepoužívejte stejný příkaz nebo objekt pro čtení dat synchronně před dokončením úkolu. Bez MARS nepoužívejte stejné připojení synchronně, dokud úloha neskončí.
Přidejte zrušení a časové limity
Zrušení asynchronní operace
Předejte stejný token zrušení od otevření spojení až po provedení příkazu:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellation.Token);
using var command = new SqlCommand(
"SELECT COUNT(*) FROM Production.Product;",
connection);
await command.ExecuteScalarAsync(cancellation.Token);
Zrušení se provádí v maximální možné míře. Operace může být dokončena dříve, než zrušení operace začne platit, a pozorovaná výjimka závisí na místě, kde ke zrušení operace dojde. Nepředpokládejte, že obdržení žádosti o zrušení znamená, že server vrátil zpět již dokončenou práci. Když musí být sada změn úspěšná nebo neúspěšná jako celek, použijte explicitní transakci. Pro více informací viz Lokální transakce.
Cancel žádá o zrušení aktuálního příkazu. Nenahrazuje to token pro zrušení vlastněný volajícím. Operace může být dokončena dříve, než žádost o zrušení začne platit.
Po zrušení nebo vypršení časového limitu vyčkejte na dokončení operace a uvolněte čtečku i příkaz. Před opětovným použitím zkontrolujte stav připojení. Selhání, které přeruší spojení, vyžaduje nové připojení.
Konfigurace časových limitů
Časové limity spojení a příkazů pokrývají různé fáze:
| Setting | Pokrývá |
|---|---|
Connect Timeout |
Navázání spojení a čekání na připojení z fondu. |
CommandTimeout |
Provádění příkazů a kumulativní čekání na čtení ze sítě. |
| Token zrušení | Žádost ovládaná volajícím o ukončení čekání. |
CommandTimeout se měří v sekundách.
0 znamená, že není nastaven žádný časový limit příkazu. Výchozí hodnota je hodnota připojení Command Timeout, je-li nakonfigurována, jinak 30 sekund.
Pro čtenáře se časový limit příkazu vztahuje na kumulativní síťové čekání každé operace čtení poskytovatele. Nezahrnuje čas strávený zpracováním řádku v aplikačním kódu. Timeout může nastat před prvním řádkem nebo při načítání pozdějšího řádku.
Zkombinujte zrušení a časový limit příkazu
Používejte konečný časový limit příkazu a rušení volajícího společně. Tento příklad předpokládá, že connection je otevřený:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
using var command = new SqlCommand("""
SELECT TOP (10) SalesOrderID, OrderDate
FROM Sales.SalesOrderHeader
ORDER BY SalesOrderID;
""", connection)
{
CommandTimeout = 15
};
await using SqlDataReader reader =
await command.ExecuteReaderAsync(cancellation.Token);
while (await reader.ReadAsync(cancellation.Token))
{
Console.WriteLine($"{reader.GetInt32(0)} | {reader.GetDateTime(1):O}");
}
Pokud volající zruší hovor jako první, čekající volání vyvolá výjimku. Typ výjimky závisí na místě, kde dochází ke zrušení operace. Pokud vyprší časový limit příkazu dříve, poskytovatel nahlásí vypršení časového limitu. V obou případech může příkaz úspěšně skončit dříve, než zrušení nebo časový limit nazačne platit.
Pokročilé asynchronní vzory
Používejte asynchronní metody přes základní typy poskytovatelů
Kód nezávislý na poskytovateli může používat DbConnection, DbCommand, a DbDataReader asynchronní metody. Vytvářejte parametry pomocí příkazu poskytovatele a vyhněte se downcastům, pokud aplikace nepotřebuje funkci specifickou pro Microsoft.Data.SqlClient.
Používejte metody transakcí SQL pro úlohy
Používejte lokální transakční metody založené na třídě Task, pokud je cílový rámec zpřístupní. Následující příklad předpokládá, že connection je otevřená, sql obsahuje text příkazu a volající předá cancellationToken.
await using SqlTransaction transaction =
(SqlTransaction)await connection.BeginTransactionAsync(cancellationToken);
using var command = new SqlCommand(sql, connection, transaction);
try
{
await command.ExecuteNonQueryAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
catch (Exception operationException)
{
try
{
await transaction.RollbackAsync(CancellationToken.None);
}
catch (Exception rollbackException)
{
throw new AggregateException(
"The operation and transaction rollback both failed.",
operationException,
rollbackException);
}
throw;
}
Pro rollback použijte samostatný token pro vyčištění. Token operace už mohl být zrušen. Zpracujte selhání vrácení zpět, aniž by byla zahozena původní výjimka operace. Tento příklad zachovává obě výjimky v prvku AggregateException. Aplikace může místo toho zaznamenat výjimku rollbacku a znovu vyhodit původní výjimku.
Tyto metody Microsoft.Data.SqlClient používají zděděné implementace DbConnection a DbTransaction, které volají synchronní transakční metody a vracejí dokončenou úlohu. Čekání na ně neblokuje volající vlákno, zatímco transakční operace běží. Zrušení se kontroluje před zahájením operace a nepřerušuje probíhající operaci. Provádění příkazů uvnitř transakce je stále asynchronní.
Používejte asynchronní distribuované transakce
Asynchronní příkazy mohou být vykonány uvnitř TransactionScope. Obor nastavuje Transaction.Current, kterou .NET nazývá ambientní transakcí. Spojení, které se otevře uvnitř rozsahu, se automaticky zařadí do této transakce, protože klíčové slovo připojení Enlist má výchozí hodnotu true. Konfigurujte TransactionScopeAsyncFlowOption.Enabled, pokud transakce musí procházet přes await. Distribuované transakce mají požadavky na platformu, server a koordinátora. Pro více informací viz integrace System.Transactions.
Používejte asynchronní operace se SqlBulkCopy
Použijte WriteToServerAsync pro asynchronní hromadné kopírování. Udržujte čtečku zdroje nebo kolekci řádků naživu, dokud se vrácený úkol nedokončí. Nastavte velikost dávky, chování transakcí a časový limit pro operaci. Pro více informací viz Hromadné kopírovací operace.
Asynchronní použití více příkazů s MARS
MARS umožňuje více než jeden čekající příkaz na jednom fyzickém spojení. Zapněte ho MultipleActiveResultSets=True pouze tehdy, když aplikace potřebuje tento model sdílení připojení.
MARS není nutný pro asynchronní programování. Bez MARS před spuštěním dalšího příkazu na stejném připojení dokončete nebo zlikvidujte aktivní čtečku. Druhé připojení je často jasnější pro skutečně nezávislou práci.
Pro chování relace a omezení transakcí viz Enable Multiple Active Result Sets.
Asynchronní čtení a aktualizace dat pomocí MARS
Když povolíte MARS, jeden příkaz může číst řádky, zatímco jiný příkaz se vykonává na stejném spojení. Čekejte na každý příkaz a operaci čtečky zvlášť. Operace MARS nejsou vláknově bezpečné a spuštění serveru se může stále serializovat v bodech předání řízení.
Pokud musí být čtení a aktualizace atomické, přiřaďte ke každému příkazu stejnou lokální transakci a otestujte chování transakce ve vztahu k MARS. Neprovádějte potvrzení transakce, dokud je jiná operace v této transakci stále nevyřízená.
Starší asynchronní programování
Microsoft.Data.SqlClient zachovává kvůli kompatibilitě metody modelu Asynchronous Programming Model (APM) BeginExecuteNonQuery, BeginExecuteReader a BeginExecuteXmlReader. Nepoužívejte je v novém kódu.
CommandTimeout je těmito starými asynchronními voláními ignorována a je respektována úkolovými metodami jako ExecuteReaderAsync.
Legacy metody nevyžadují Asynchronous Processing=true. Microsoft. Data.SqlClient 4.0 a novější verze vlastnost Asynchronous Processingconnection nepodporují.
Související obsah
- Začněte synchronním workflow spuštění příkazů.
- Pro zpracování řádků získávejte data pomocí SqlDataReader.
- Pro přenos velkých hodnot použijte podporu streamování přes SqlClient.
- Pro chování relací a transakcí v MARS viz Multiple Active Result Sets.