Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Utilize métodos assíncronos baseados em tarefas para evitar bloquear uma thread enquanto a Microsoft.Data.SqlClient espera pela ligação e pela E/S de rede. A execução assíncrona não torna um comando ou leitor seguro para uso simultâneo, nem reduz o tempo de execução da consulta no servidor.
Os exemplos que consultam tabelas de aplicação utilizam a base de dados de exemplo AdventureWorks. Os exemplos de C# requerem using Microsoft.Data.SqlClient;.
Converter um percurso de chamadas para código assíncrono
Converta um caminho de chamada completo de cada vez:
- Torne o método envolvente assíncrono.
- Substitua chamadas de conexão, comando, reader e transação pelos seus equivalentes baseados em tarefas.
-
awaitLimpe cada operação antes de usar o mesmo objeto novamente. - Passe o token de cancelamento do chamador por todas as operações que o chamador possa cancelar.
- Mantenha um tempo limite de comando finito como limite de operação da base de dados.
Utilize await desde a abertura da ligação até ao processamento de resultados. O excerto seguinte assume que connectionString e sql já estão definidos. Cria um CancellationTokenSource com um tempo limite finito para a operação:
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 retorna quando o primeiro resultado está disponível, não depois de todas as linhas chegarem. Cada chamada a ReadAsync avança uma linha, pelo que o loop processa as linhas à medida que chegam e liberta o thread apenas enquanto espera por mais dados do servidor. O token de 30 segundos limita toda a operação, incluindo o loop de leitura.
Apenas uma execução assíncrona pode estar pendente num SqlCommand. Apenas uma leitura assíncrona, transição de resultado ou operação de campo pode estar pendente num SqlDataReader. Aguarde cada operação antes de iniciar a próxima operação no mesmo objeto.
Recursos de programação assíncrona
Os seguintes métodos síncronos têm equivalentes baseados em tarefas que aceitam um token de cancelamento:
| Método síncrono | Método assíncrono | Example |
|---|---|---|
SqlConnection.Open |
SqlConnection.OpenAsync |
Converta um método síncrono |
SqlCommand.ExecuteNonQuery |
SqlCommand.ExecuteNonQueryAsync |
Executar comandos assincronamente |
SqlCommand.ExecuteScalar |
SqlCommand.ExecuteScalarAsync |
Executar comandos de forma assíncrona |
SqlCommand.ExecuteXmlReader |
SqlCommand.ExecuteXmlReaderAsync |
Executar comandos de forma assíncrona |
SqlCommand.ExecuteReader |
SqlCommand.ExecuteReaderAsync |
Converter o caminho de chamada para código assíncrono |
SqlDataReader.Read |
SqlDataReader.ReadAsync |
Converter um caminho de chamada em código assíncrono |
SqlDataReader.NextResult |
SqlDataReader.NextResultAsync |
Leia os resultados de forma assíncrona |
SqlDataReader.IsDBNull |
SqlDataReader.IsDBNullAsync |
Leia os resultados de forma assíncrona |
SqlDataReader.GetFieldValue<T> |
SqlDataReader.GetFieldValueAsync<T> |
Leia os resultados de forma assíncrona |
SqlBatch.ExecuteNonQuery, ExecuteScalare ExecuteReader |
SqlBatch.ExecuteNonQueryAsync, ExecuteScalarAsynce ExecuteReaderAsync |
Executar um lote |
SqlBulkCopy.WriteToServer |
SqlBulkCopy.WriteToServerAsync |
Utilize operações assíncronas com o SqlBulkCopy |
Utilize a libertação síncrona quando um objeto implementa apenas IDisposable. Utilize await using para objetos que implementem IAsyncDisposable. Descarte o reader antes de reutilizar a sua conexão.
Executar comandos assincronamente
Os exemplos seguintes assumem que connection está aberto. Utilize ExecuteScalarAsync para um único valor e passe um token de cancelamento pertencente ao chamador:
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}");
Utilize ExecuteNonQueryAsync para instruções que alteram dados. Use ExecuteXmlReaderAsync para uma FOR XML consulta. Elimine um leitor XML antes de executar outro comando na mesma conexão. Sem Múltiplos Conjuntos de Resultados Ativos (MARS), uma conexão permite apenas um reader aberto.
Leia os resultados de forma assíncrona
O exemplo seguinte passa o mesmo token de cancelamento a cada uma destas chamadas assíncronas:
-
ExecuteReaderAsyncExecuta a consulta. -
ReadAsyncLê cada linha. -
GetFieldValueAsynclê o valor de cada coluna. -
NextResultAsyncpassa para o próximo conjunto de resultados. -
IsDBNullAsyncverificaNULL.
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 passa para o próximo conjunto de resultados.
GetFieldValueAsync e IsDBNullAsync leem uma coluna da linha atual. Quando usas a predefinição CommandBehavior, a linha já está em memória intermédia, por isso estas chamadas normalmente são concluídas sem esperar. Quando usas CommandBehavior.SequentialAccess, eles podem aguardar os dados da coluna da rede. Para mais informações sobre acesso sequencial, consulte suporte a streaming SqlClient.
Converta um método síncrono
Este método síncrono abre uma ligação, executa um comando e devolve o número de linhas em 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()!;
}
Para converter o método para código assíncrono, faça estas alterações:
- Mude o tipo de retorno para
Task<int>. - Adiciona um
CancellationTokenparâmetro. - Substitua
OpenporOpenAsync, e substituaExecuteScalarporExecuteScalarAsync. - Liberte a ligação com
await using.
O método convertido é o seguinte:
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))!;
}
O chamador cria o token e aguarda o resultado:
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));
int objectCount = await CountObjectsAsync(connectionString, cancellation.Token);
Console.WriteLine($"Objects: {objectCount}");
Ambos os métodos devolvem a mesma contagem. A saída depende da base de dados a que se liga.
Connect Timeout Ainda limita o estabelecimento de ligação e os tempos de espera do pool. Cancelar o token pode devolver o controlo ao chamador mais cedo.
Adicionar chamadas assíncronas a uma aplicação existente
Converta um caminho de chamada completo de cada vez. Evite chamar .Result, .Wait() ou GetAwaiter().GetResult() em tarefas de base de dados porque essas chamadas bloqueiam e podem causar um interbloqueio em ambientes com contexto de sincronização.
Não inicies um comando assíncrono e depois uses o mesmo comando ou leitor de forma síncrona antes da tarefa terminar. Sem MARS, não uses a mesma ligação de forma síncrona antes da tarefa terminar.
Adicionar cancelamento e limites de tempo
Cancelar uma operação assíncrona
Passar o mesmo token de cancelamento desde a abertura da ligação até à execução do comando:
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);
O cancelamento é efetuado na medida do possível. A operação pode terminar antes de o cancelamento entrar em vigor, e a exceção observada depende do local onde o cancelamento ocorre. Não presuma que receber um pedido de cancelamento significa que o servidor reverteu o trabalho concluído. Quando um conjunto de alterações tem de ter sucesso ou falhar como unidade, utilize-se uma transação explícita. Para mais informações, consulte transações locais.
Cancel solicita o cancelamento do comando atual. Não substitui um token de cancelamento detido pelo chamador. A operação pode terminar antes de o pedido de cancelamento entrar em vigor.
Após cancelamento ou timeout, aguarde a operação e liberte o leitor e o comando. Inspeciona o estado da ligação antes de reutilizar. Uma falha que interrompe a ligação requer uma nova ligação.
Configurar os tempos de espera
Os tempos de ligação e os tempos limite dos comandos cobrem diferentes fases:
| Setting | Capas |
|---|---|
Connect Timeout |
Estabelecimento de ligação e espera por uma ligação em pool. |
CommandTimeout |
Execução de comandos e espera cumulativa de leitura de rede. |
| Token de cancelamento | Um pedido controlado pelo autor da chamada para interromper a espera. |
CommandTimeout é medido em segundos.
0 significa que não há limite de tempo de comando. O valor padrão é o valor da Command Timeout ligação quando configurada, caso contrário 30 segundos.
Para um leitor, o timeout do comando aplica-se à espera cumulativa de rede de cada operação de leitura do provedor. Não inclui o tempo gasto a processar uma linha no código da aplicação. Pode ocorrer um tempo de espera antes da primeira linha ou durante a busca de uma linha posterior.
Combinar cancelamento e tempo limite do comando
Usa um tempo limite de comando e cancelamento pelo chamador em conjunto. Este exemplo assume que connection está aberto:
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}");
}
Se o chamador cancelar primeiro, a chamada aguardada lança uma exceção. O tipo de exceção depende do local onde ocorre o cancelamento. Se o timeout do comando expirar primeiro, o fornecedor reporta um timeout. Em qualquer dos casos, o comando pode terminar com sucesso antes de o cancelamento ou timeout entrar em vigor.
Padrões assíncronos avançados
Utilizar métodos assíncronos através dos tipos de fornecedor base
O código independente do fornecedor pode usar os métodos assíncronos DbConnection, DbCommand e DbDataReader. Crie parâmetros através do comando do fornecedor e evite downcasts, a menos que a aplicação precise de uma funcionalidade específica do Microsoft.Data.SqlClient.
Utilizar métodos de transação SQL baseados em tarefas
Use os métodos de transação local baseados em tarefas quando o framework alvo os expor. O exemplo seguinte assume que connection está aberto, sql detém o texto do comando, e o chamador passa 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;
}
Utilize um token de limpeza separado para o rollback. O token de operação pode já estar cancelado. Lidar com uma falha de rollback sem descartar a exceção original da operação. Este exemplo preserva ambas as exceções num AggregateException. Uma aplicação pode, em vez disso, registar a exceção de rollback e relançar a exceção original.
Estes métodos Microsoft.Data.SqlClient usam as implementações herdadas DbConnection e DbTransaction, que chamam os métodos de transação síncrona e devolvem uma tarefa concluída. Aguardar por eles não libera a thread chamadora enquanto a operação de transação decorre. O cancelamento é verificado antes do início da operação e não interrompe uma operação em andamento. A execução de comandos dentro da transação continua a ser assíncrona.
Utilizar transações distribuídas assíncronas
Comandos assíncronos podem ser executados dentro de um TransactionScope. O âmbito estabelece Transaction.Current, que o .NET chama de transação ambiante. Uma ligação que se abre no âmbito é automaticamente associada a essa transação, porque a palavra-chave de ligação Enlist tem por predefinição o valor true. Configure TransactionScopeAsyncFlowOption.Enabled quando a transação deve fluir através de await. As transações distribuídas têm requisitos de plataforma, servidor e coordenador. Para mais informações, consulte integração System.Transactions.
Utilize operações assíncronas com o SqlBulkCopy
Utilize WriteToServerAsync para cópia assíncrona em massa. Mantenha o leitor de origem ou a coleção de linhas disponível até que a tarefa devolvida seja concluída. Configura o tamanho do lote, o comportamento das transações e o tempo limite para a operação. Para mais informações, consulte Operações de cópia em massa.
Use vários comandos de forma assíncrona com o MARS
O MARS permite mais do que um comando pendente numa única ligação física. Ative-o MultipleActiveResultSets=True apenas quando a aplicação precisar desse modelo de partilha de ligações.
O MARS não é necessário para programação assíncrona. Sem MARS, termine ou descarte o leitor ativo antes de executar outro comando na mesma conexão. Uma segunda conexão é muitas vezes mais clara para trabalhos verdadeiramente independentes.
Para comportamentos de sessão e restrições de transação, consulte Ativar Múltiplos Conjuntos de Resultados Ativos.
Ler e atualizar dados de forma assíncrona com o MARS
Quando ativas o MARS, um comando pode ler linhas enquanto outro comando é executado na mesma conexão. Aguarde cada comando e operação do leitor separadamente. As operações MARS não são seguras para threads, e a execução do servidor ainda pode serializar nos yield points.
Quando as operações de leitura e atualização devem ser atómicas, atribui-se a mesma transação local a cada comando e teste o comportamento MARS da transação. Não faça commit enquanto outra operação nessa transação ainda estiver pendente.
Programação assíncrona herdada
Microsoft. Data.SqlClient mantém os métodos BeginExecuteNonQuerydo Modelo de Programação Assíncrona (APM) , BeginExecuteReader, e BeginExecuteXmlReader para compatibilidade. Não os uses em código novo.
CommandTimeout é ignorado por estas chamadas assíncronas antigas e é honrado por métodos baseados em tarefas, como ExecuteReaderAsync.
Os métodos legados não requerem Asynchronous Processing=true. Microsoft. Data.SqlClient 4.0 e versões posteriores não suportam a Asynchronous Processing propriedade de ligação.
Conteúdo relacionado
- Comece pelo fluxo de trabalho síncrono de execução de comandos.
- Para processar linhas, recupere dados com o SqlDataReader.
- Para transferir valores grandes, use suporte a streaming SqlClient.
- Para o comportamento de sessões e transações do MARS, veja Múltiplos Conjuntos de Resultados Ativos.