Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Use métodos assíncronos baseados em tarefas para evitar bloquear uma thread enquanto Microsoft.Data.SqlClient aguarda a conexão e a E/S de rede. A execução assíncrona não torna um comando ou leitor seguro para uso concorrente, e não reduz o tempo de execução da consulta no servidor.
Os exemplos que consultam tabelas de aplicação utilizam o banco de dados de exemplo AdventureWorks. Os exemplos de C# requerem using Microsoft.Data.SqlClient;.
Converta um fluxo de chamadas em código assíncrono
Converta um caminho de chamada completo por vez:
- Torne assíncrono o método que o contém.
- Substitua chamadas de conexão, comando, reader e transação por suas contrapartes baseadas em tarefas.
-
awaitconclua cada operação antes de usar o mesmo objeto novamente. - Passe o token de cancelamento do chamador por toda operação que ele possa cancelar.
- Mantenha um tempo limite de comando como limite de operação de banco de dados.
Use await desde a abertura da conexão até o processamento dos resultados. O trecho a seguir assume que connectionString e sql já estão definidos. Ele 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 que todas as linhas chegam. Cada ReadAsync chamada avança uma linha, então o loop processa as linhas à medida que chegam e libera a thread apenas enquanto espera 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 em um SqlCommand. Apenas uma leitura assíncrona, transição de resultado ou operação de campo pode estar pendente em um 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 possuem 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 de forma assíncrona |
SqlCommand.ExecuteScalar |
SqlCommand.ExecuteScalarAsync |
Executar comandos de forma assíncrona |
SqlCommand.ExecuteXmlReader |
SqlCommand.ExecuteXmlReaderAsync |
Executar comandos de forma assíncrona |
SqlCommand.ExecuteReader |
SqlCommand.ExecuteReaderAsync |
Converta um fluxo de chamadas em código assíncrono |
SqlDataReader.Read |
SqlDataReader.ReadAsync |
Converta um fluxo de chamadas 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, ExecuteScalar e ExecuteReader |
SqlBatch.ExecuteNonQueryAsync, ExecuteScalarAsync e ExecuteReaderAsync |
Executar um lote |
SqlBulkCopy.WriteToServer |
SqlBulkCopy.WriteToServerAsync |
Usar operações assíncronas com SqlBulkCopy |
Use o descarte síncrono quando um objeto implementa apenas IDisposable. Use await using para objetos que implementam IAsyncDisposable. Descarte o leitor antes de reutilizar sua conexão.
Executar comandos de forma assíncrona
Os exemplos a seguir assumem que connection está aberto. Use 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}");
Use ExecuteNonQueryAsync para instruções que alteram dados. Use ExecuteXmlReaderAsync para uma consulta FOR XML. Descarte 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 leitor aberto.
Leia os resultados de forma assíncrona
O exemplo a seguir passa o mesmo token de cancelamento para cada uma dessas chamadas assíncronas:
-
ExecuteReaderAsyncexecuta a consulta. -
ReadAsynclê cada linha. -
GetFieldValueAsynclê o valor de cada coluna. -
NextResultAsyncavança para o próximo conjunto de resultados. -
IsDBNullAsyncverifica se háNULL.
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 avança para o próximo conjunto de resultados.
GetFieldValueAsync e IsDBNullAsync leem uma coluna da linha atual. Quando você usa o padrão CommandBehavior, a linha já está em buffer, então essas chamadas geralmente são concluídas sem esperar. Quando você usa CommandBehavior.SequentialAccess, eles podem aguardar dados de coluna da rede. Para mais informações sobre acesso sequencial, veja suporte a streaming SqlClient.
Como converter um método síncrono
Esse método síncrono abre uma conexão, executa um comando e retorna 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 em código assíncrono, faça estas alterações:
- Mude o tipo de retorno para
Task<int>. - Adicione um
CancellationTokenparâmetro. - Substitua
OpenporOpenAsync, e substituaExecuteScalarporExecuteScalarAsync. - Descarte a conexã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 retornam a mesma contagem. A saída depende do banco de dados ao qual você se conecta.
Connect Timeout ainda limita o estabelecimento de conexão e as esperas no pool de conexões. Cancelar o token pode devolver o controle ao chamador mais cedo.
Adicionar chamadas assíncronas a uma aplicação existente
Converta um caminho completo de chamada por vez. Evite chamar .Result, .Wait() ou GetAwaiter().GetResult() em tarefas de banco de dados porque essas chamadas bloqueiam e podem causar deadlock em ambientes com contexto de sincronização.
Não inicie um comando assíncrono e depois use o mesmo comando ou leitor de forma síncrona antes do término da tarefa. Sem MARS, não use a mesma conexão de forma síncrona antes do término da tarefa.
Adicionar cancelamento e limites de tempo
Cancelar uma operação assíncrona
Passe o mesmo token de cancelamento desde a abertura da conexão até a 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 é realizado da melhor forma possível. A operação pode terminar antes que o cancelamento entre em vigor, e a exceção observada depende de 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 mudanças precisa ter sucesso ou falhar como unidade, use uma transação explícita. Para mais informações, veja Transações Locais.
Cancel solicita o cancelamento do comando atual. Ele não substitui um token de cancelamento de propriedade do chamador. A operação pode ser concluída antes que o pedido de cancelamento tenha efeito.
Após o cancelamento ou tempo limite, aguarde a operação e libere o reader e o command. Inspecione o estado da conexão antes de reutilizar. Uma falha que quebra a conexão exige uma nova conexão.
Configurar tempos limite
Os tempos de conexão e timeouts de comando cobrem diferentes fases:
| Setting | Cobre |
|---|---|
Connect Timeout |
Estabelecimento de conexão e espera por uma conexão do pool. |
CommandTimeout |
Execução de comandos e esperas cumulativas de leitura de rede. |
| Token de cancelamento | Um pedido controlado pelo chamador para parar de esperar. |
CommandTimeout é medido em segundos.
0 significa que não há limite de tempo de comando. O padrão é o valor da Command Timeout conexão quando configurada, caso contrário, 30 segundos.
Para um leitor, o tempo limite do comando se aplica à espera cumulativa de rede de cada operação de leitura do provedor. Não inclui o tempo gasto processando uma linha no código da aplicação. Um tempo limite pode ocorrer antes da primeira linha ou ao buscar uma linha posterior.
Combinar o cancelamento e o tempo limite do comando
Use um tempo limite finito para o comando e o cancelamento pelo chamador juntos. 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 de onde ocorre o cancelamento. Se o timeout do comando expirar primeiro, o provedor reporta um timeout. Em ambos os casos, o comando pode ser concluído com sucesso antes que o cancelamento ou timeout entre em vigor.
Padrões assíncronos avançados
Use métodos assíncronos por meio dos tipos de provedores base
Código independente do provedor pode usar os métodos assíncronos DbConnection, DbCommand e DbDataReader. Crie parâmetros através do comando provider e evite downcasts, a menos que o aplicativo precise de um recurso específico do Microsoft.Data.SqlClient.
Use métodos de transação SQL baseados em tarefas
Utilize os métodos de transação local baseados em tarefas quando o framework de destino os expor. O exemplo a seguir assume que connection está aberto, sql conté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;
}
Use um cleanup token separado para rollback. O token de operação pode já estar cancelado. Lidar com uma falha de rollback sem descartar a exceção original de operação. Este exemplo preserva ambas as exceções em um AggregateException. Uma aplicação pode, em vez disso, registrar a exceção de rollback e relançar a exceção original.
Esses métodos Microsoft.Data.SqlClient usam as implementações herdadas DbConnection e DbTransaction, que chamam os métodos de transação síncrona e retornam uma tarefa concluída. Aguardar por eles não libera a thread de chamada enquanto a operação de transação está em andamento. 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 ainda é assíncrona.
Utilize transações distribuídas assíncronas
Comandos assíncronos podem ser executados dentro de um TransactionScope. O escopo define Transaction.Current, que o .NET chama de transação implícita. Uma conexão que se abre dentro do escopo é associada automaticamente a essa transação, porque a palavra-chave de conexão Enlist tem como padrão true. Configure TransactionScopeAsyncFlowOption.Enabled quando a transação deve fluir através de await. Transações distribuídas possuem requisitos de plataforma, servidor e coordenador. Para mais informações, veja integração com System.Transactions.
Usar operações assíncronas com SqlBulkCopy
Use WriteToServerAsync para cópia assíncrona em massa. Mantenha o leitor de origem ou a coleção de linhas viva até que a tarefa retornada seja concluída. Configure o tamanho do lote, comportamento das transações e tempo limite para a operação. Para mais informações, veja Operações de cópia em massa.
Usar vários comandos de modo assíncrono com o MARS
O MARS permite mais de um comando pendente em uma única conexão física. Ative-o MultipleActiveResultSets=True apenas quando o aplicativo precisar desse modelo de compartilhamento de conexã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. Um segundo vínculo costuma ser mais claro para trabalhos verdadeiramente independentes.
Para comportamento de sessão e restrições de transação, veja Enable Multiple Active Result Sets.
Ler e atualizar dados de modo assíncrono com o MARS
Quando você ativa 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 pontos de yield.
Quando as operações de leitura e atualização devem ser atômicas, atribua 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 naquela 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 use em código novo.
CommandTimeout é ignorado por essas chamadas assíncronas antigas e é honrado por métodos baseados em tarefas, como ExecuteReaderAsync.
Os métodos legados não exigem Asynchronous Processing=true. Microsoft. Data.SqlClient 4.0 e versões posteriores não suportam a Asynchronous Processing propriedade de conexã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 do SqlClient.
- Para o comportamento de sessões e transações do MARS, veja Multiple Active Result Sets.