Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Gebruik taakgebaseerde asynchrone methoden om te voorkomen dat je een thread blokkeert terwijl Microsoft.Data.SqlClient wacht op verbinding en netwerk-I/O. Asynchrone uitvoering maakt een commando of lezer niet veilig voor gelijktijdig gebruik, en vermindert de uitvoeringstijd van query's op de server niet.
De voorbeelden die applicatietabellen opvragen, gebruiken de AdventureWorks-voorbeelddatabase. De C#-voorbeelden vereisen using Microsoft.Data.SqlClient;.
Converteer een aanroepketen naar asynchrone code
Converteer één volledig oproeppad tegelijk:
- Maak de methode waarin dit staat asynchroon.
- Vervang verbindings-, commando-, lezer- en transactieaanroepen door hun taakgebaseerde tegenhangers.
-
awaitelke bewerking voordat u hetzelfde object opnieuw gebruikt. - Geef het annuleringstoken van de aanroeper door aan elke bewerking die de aanroeper kan annuleren.
- Houd een eindige commando-timeout als limiet voor database-operaties.
Gebruik await vanaf het openen van de verbinding tot resultaatverwerking. Het volgende fragment gaat ervan uit dat connectionString en sql al gedefinieerd zijn. Het creëert een CancellationTokenSource met een eindige timeout voor de bewerking:
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));
}
ExecuteReaderAsync keert terug wanneer het eerste resultaat beschikbaar is, niet nadat alle rijen zijn aangekomen. Elke ReadAsync aanroep schuift één rij vooruit, zodat de lus rijen verwerkt zodra ze binnenkomen en de thread alleen bezet houdt terwijl de lus op meer data van de server wacht. De 30-seconden token beperkt de hele operatie, inclusief de leeslus.
Er kan slechts één asynchrone uitvoering in wacht zijn op een SqlCommand. Slechts één asynchrone lees-, resultaatovergang of veldoperatie kan in wacht zijn op een SqlDataReader. Wacht op elke bewerking voordat je de volgende bewerking op hetzelfde object start.
Functies voor asynchroon programmeren
De volgende synchrone methoden hebben taakgebaseerde tegenhangers die een annuleringstoken accepteren:
| Synchrone methode | Asynchrone methode | Example |
|---|---|---|
SqlConnection.Open |
SqlConnection.OpenAsync |
Converteer een synchrone methode |
SqlCommand.ExecuteNonQuery |
SqlCommand.ExecuteNonQueryAsync |
Voer commando's asynchroon uit |
SqlCommand.ExecuteScalar |
SqlCommand.ExecuteScalarAsync |
Voer commando's asynchroon uit |
SqlCommand.ExecuteXmlReader |
SqlCommand.ExecuteXmlReaderAsync |
Voer commando's asynchroon uit |
SqlCommand.ExecuteReader |
SqlCommand.ExecuteReaderAsync |
Zet een aanroepketen om in asynchrone code |
SqlDataReader.Read |
SqlDataReader.ReadAsync |
Converteer een aanroeppad naar asynchrone code |
SqlDataReader.NextResult |
SqlDataReader.NextResultAsync |
Lees de resultaten asynchroon |
SqlDataReader.IsDBNull |
SqlDataReader.IsDBNullAsync |
Resultaten asynchroon lezen |
SqlDataReader.GetFieldValue<T> |
SqlDataReader.GetFieldValueAsync<T> |
Lees de resultaten asynchroon |
SqlBatch.ExecuteNonQuery, ExecuteScalar en ExecuteReader |
SqlBatch.ExecuteNonQueryAsync, ExecuteScalarAsync en ExecuteReaderAsync |
Voer een batch uit |
SqlBulkCopy.WriteToServer |
SqlBulkCopy.WriteToServerAsync |
Gebruik asynchrone bewerkingen met SqlBulkCopy |
Gebruik synchrone opschoning wanneer een object alleen IDisposable implementeert. Gebruik await using voor objecten die IAsyncDisposable implementeren. Geef de lezer vrij voordat je de verbinding hergebruikt.
Voer commando's asynchroon uit
De volgende voorbeelden gaan ervan uit dat connection open is. Gebruik ExecuteScalarAsync voor één enkele waarde en geef een door de aanroeper beheerd annuleringstoken door:
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}");
Gebruik ExecuteNonQueryAsync voor data-veranderende statements. Gebruik ExecuteXmlReaderAsync voor een FOR XML query. Sluit een XML-lezer voordat je een ander commando uitvoert op dezelfde connectie. Zonder Multiple Active Result Sets (MARS) staat een verbinding slechts één geopende DataReader toe.
Lees de resultaten asynchroon
Het volgende voorbeeld geeft hetzelfde annuleringstoken door aan elk van deze asynchrone aanroepen:
-
ExecuteReaderAsyncvoert de query uit. -
ReadAsyncleest elke rij. -
GetFieldValueAsyncleest elke kolomwaarde. -
NextResultAsyncgaat naar de volgende resultatenset. -
IsDBNullAsynccontroleert opNULL.
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 gaat naar de volgende resultaatset.
GetFieldValueAsync en IsDBNullAsync lezen één kolom van de huidige rij. Wanneer je de standaard CommandBehaviorgebruikt, is de regel al gebuffreerd, dus deze aanroepen worden meestal voltooid zonder te wachten. Wanneer je CommandBehavior.SequentialAccess gebruikt, kunnen ze wachten op kolomgegevens van het netwerk. Voor meer informatie over sequentiële toegang, zie SqlClient streaming support.
Converteer een synchrone methode
Deze synchrone methode opent een verbinding, voert een commando uit en geeft het aantal rijen in sys.objectsterug:
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()!;
}
Om de methode naar asynchrone code om te zetten, breng je deze wijzigingen aan:
- Verander het retourtype naar
Task<int>. - Voeg een
CancellationTokenparameter toe. - Vervang
OpendoorOpenAsync, en vervangExecuteScalarmetExecuteScalarAsync. - Geef de verbinding met
await usingvrij.
De geconverteerde methode ziet er als volgt uit:
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))!;
}
De aanroeper maakt het token aan en wacht op het resultaat:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
int objectCount = await CountObjectsAsync(connectionString, cancellation.Token);
Console.WriteLine($"Objects: {objectCount}");
Beide methoden geven hetzelfde aantal terug. De output hangt af van de database waar je verbinding mee maakt.
Connect Timeout beperkt nog steeds het opzetten van verbindingen en wachttijden in de connection pool. Het annuleren van het token kan de controle sneller teruggeven aan de aanroeper.
Voeg asynchrone aanroepen toe aan een bestaande applicatie
Converteer één volledig oproeppad tegelijk. Vermijd het aanroepen van .Result, .Wait() of GetAwaiter().GetResult() bij databasetaken, omdat die aanroepen blokkeren en kunnen vastlopen in omgevingen met een synchronisatiecontext.
Start geen asynchroon commando en gebruik dan hetzelfde commando of dezelfde lezer synchroon voordat de taak klaar is. Zonder MARS moet je dezelfde verbinding niet synchroon gebruiken voordat de taak is afgerond.
Voeg annulering en tijdslimieten toe
Een asynchrone bewerking annuleren
Geef hetzelfde annuleringstoken door vanaf het openen van de verbinding tot en met de uitvoering van de opdracht:
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);
Annuleren gebeurt op basis van best effort. De bewerking kan worden voltooid voordat de onderbreking van kracht wordt, en de waargenomen uitzondering hangt af van waar de onderbreking wordt uitgevoerd. Ga er niet van uit dat het ontvangen van een annuleringsverzoek betekent dat de server voltooid werk heeft teruggedraaid. Wanneer een set wijzigingen als eenheid moet slagen of falen, gebruik dan een expliciete transactie. Voor meer informatie, zie Lokale transacties.
Cancel verzoekt annulering van de huidige opdracht. Het vervangt geen cancellation token dat eigendom is van de aanroeper. De bewerking kan worden afgerond voordat het annuleringsverzoek van kracht wordt.
Na annulering of timeout wacht je op de bewerking en geef je de reader en het commando vrij. Controleer de verbindingsstatus voordat je deze opnieuw gebruikt. Een storing waardoor de verbinding wordt verbroken, vereist een nieuwe verbinding.
Het configureren van time-outs
Verbindings- en commando-timeouts dekken verschillende fasen:
| Setting | Omvat |
|---|---|
Connect Timeout |
Brengt een verbinding tot stand en wacht op een verbinding uit de pool. |
CommandTimeout |
Commando-uitvoering en cumulatieve wachttijden bij netwerklezen. |
| Annuleringstoken | Een door de beller gecontroleerd verzoek om te stoppen met wachten. |
CommandTimeout wordt gemeten in seconden.
0 betekent geen tijdslimiet voor opdrachten. De standaard is de Command Timeout waarde van de verbinding indien geconfigureerd, anders 30 seconden.
Voor een lezer geldt de commando-timeout voor de cumulatieve netwerkwachttijd van elke provider-leesoperatie. Het omvat geen tijd die wordt besteed aan het verwerken van een record in applicatiecode. Een time-out kan plaatsvinden vóór de eerste rij of tijdens het ophalen van een latere rij.
Combineren van annulering en commando-time-out
Gebruik een beperkte commando-time-out en annulering door de aanroeper samen. Dit voorbeeld gaat ervan uit dat connection open is:
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}");
}
Als de beller als eerste annuleert, gooit de aanroep waarop wordt gewacht een uitzondering. Het exceptietype hangt af van waar het annuleren plaatsvindt. Als de commando-timeout eerst verloopt, meldt de provider een time-out. In beide gevallen kan het commando succesvol worden afgerond voordat de annulering of time-out in werking treedt.
Geavanceerde asynchrone patronen
Gebruik asynchrone methoden via basisprovidertypes
Provider-onafhankelijke code kan gebruikmaken van DbConnection, DbCommand, en DbDataReader asynchrone methoden. Maak parameters aan via het provider-commando en vermijd downcasts tenzij de applicatie een Microsoft.Data.SqlClient-specifieke functie nodig heeft.
Gebruik taakgebaseerde SQL-transactiemethoden
Gebruik de taakgebaseerde lokale transactiemethoden wanneer het doelframework deze blootstelt. Het volgende voorbeeld gaat ervan uit dat connection open is, sql de commandotekst vasthoudt, en de aanroeper geeft cancellationTokendoor :
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;
}
Gebruik een apart opruimtoken voor rollback. Het operatietoken kan al geannuleerd zijn. Behandel een rollback-fout zonder de oorspronkelijke operatie-uitzondering te verwerpen. Dit voorbeeld behoudt beide uitzonderingen in een AggregateException. Een applicatie kan in plaats daarvan de rollback-uitzondering loggen en de oorspronkelijke uitzondering opnieuw gooien.
Deze Microsoft. Data.SqlClient-methoden gebruiken de geërfde DbConnection en DbTransaction implementaties, die de synchrone transactiemethoden aanroepen en een voltooide taak teruggeven. Door erop te wachten wordt de aanroepende thread niet vrijgegeven terwijl de transactie loopt. Annuleren wordt gecontroleerd voordat de bewerking begint en onderbreekt geen lopende bewerking. De uitvoering van commando's binnen de transactie is nog steeds asynchroon.
Gebruik asynchrone gedistribueerde transacties
Asynchrone commando's kunnen binnen een TransactionScope worden uitgevoerd. De scope stelt Transaction.Current in, wat .NET de ambient-transactie noemt. Een verbinding die binnen de scope wordt geopend, wordt automatisch opgenomen in die transactie, omdat het Enlist verbindingszoekwoord standaard is ingesteld op true. Configureer TransactionScopeAsyncFlowOption.Enabled wanneer de transactie zich over await moet uitstrekken. Gedistribueerde transacties hebben platform-, server- en coördinatorvereisten. Voor meer informatie, zie System.Transactions-integratie.
Gebruik asynchrone bewerkingen met SqlBulkCopy
Gebruik WriteToServerAsync voor asynchrone bulkkopieën. Houd de bronlezer of rijcollectie actief totdat de teruggestuurde taak is voltooid. Configureer de batchgrootte, het transactiegedrag en de time-out voor de bewerking. Voor meer informatie, zie bulk copy-bewerkingen.
Asynchroon meerdere opdrachten gebruiken met MARS
MARS staat meer dan één wachtende opdracht toe op één fysieke verbinding. Schakel het met MultipleActiveResultSets=True alleen in wanneer de applicatie dat verbindingsdelingsmodel nodig heeft.
MARS is niet vereist voor asynchrone programmering. Zonder MARS rond je de actieve reader af of geef je de actieve reader vrij voordat je een ander commando op dezelfde verbinding uitvoert. Een tweede aansluiting is vaak duidelijker bij echt zelfstandig werk.
Voor sessiegedrag en transactiebeperkingen, zie Meerdere actieve resultaatsets inschakelen.
Asynchroon gegevens lezen en bijwerken met MARS
Wanneer je MARS inschakelt, kan het ene commando rijen lezen terwijl het andere commando op dezelfde verbinding wordt uitgevoerd. Wacht afzonderlijk op elk commando en elke lezerbewerking. MARS-operaties zijn niet thread-veilig, en uitvoering op de server kan nog steeds serialiseren op yield points.
Wanneer lees- en updateoperaties atomair moeten zijn, wijs je aan elk commando dezelfde lokale transactie toe en test je het MARS-gedrag van de transactie. Voer geen commit uit terwijl een andere bewerking in die transactie nog in behandeling is.
Verouderde asynchrone programmering
Microsoft. Data.SqlClient behoudt de Asynchronous Programming Model (APM)-methoden BeginExecuteNonQuery, BeginExecuteReader, en BeginExecuteXmlReader voor compatibiliteit. Gebruik ze niet in nieuwe code.
CommandTimeout wordt genegeerd door deze ouderwetse asynchrone aanroepen en wordt gerespecteerd door taakgebaseerde methoden zoals ExecuteReaderAsync.
De legacy-methoden vereisen geen Asynchronous Processing=true. Microsoft. Data.SqlClient 4.0 en latere versies ondersteunen de Asynchronous Processing verbindingseigenschap niet.
Verwante onderwerpen
- Begin met de synchrone commando-uitvoeringsworkflow.
- Om rijen te verwerken, haal je data op met SqlDataReader.
- Om grote waarden over te zetten, gebruik SqlClient streaming-ondersteuning.
- Voor MARS-sessie- en transactiegedrag, zie Multiple Active Result Sets.