CancellationToken с асинхронными методами Dapper?

Я использую Dapper 1.31 от Nuget. У меня есть этот очень простой фрагмент кода,

string connString = "";
string query = "";
int val = 0;
CancellationTokenSource tokenSource = new CancellationTokenSource();
using (IDbConnection conn = new SqlConnection(connString))
{
    conn.Open();
    val = (await conn.QueryAsync<int>(query, tokenSource.Token)).FirstOrDefault();
}

Когда я нажимаю F12 на QueryAsync, он указывает мне на

public static Task<IEnumerable<T>> QueryAsync<T>
     (
        this IDbConnection cnn, 
        string sql, 
        dynamic param = null, 
        IDbTransaction transaction = null, 
        int? commandTimeout = null, 
        CommandType? commandType = null
     );

В его сигнатуре нет CancellationToken.

Вопросы:

  • Почему весь фрагмент полностью строится, предполагая, что ошибка компилятора на всем решении отсутствует?
  • Простите меня, поскольку я не могу проверить, действительно ли вызов tokenSource.Cancel() действительно отменяет этот метод, потому что я не знаю, как создать длинный SQL-запрос. Будет ли .Cancel() действительно отменять метод и выбрасывает OperationCancelledException?

Спасибо!

Ответ 1

Вы передаете токен отмены в качестве объекта параметра; это не сработает.

Первые асинхронные методы в dapper не отображали токен отмены; когда я пытался добавить их в качестве необязательного параметра (в качестве отдельной перегрузки, чтобы не нарушать существующие сборки), вещи очень запутались в проблемах компиляции "двусмысленного метода" . Следовательно, мне пришлось разоблачить это через отдельный API; введите CommandDefinition:

val = (await conn.QueryAsync<int>(
    new CommandDefinition(query, cancellationToken: tokenSource.Token)
).FirstOrDefault();

Затем он передает токен отмены по цепочке ко всем ожидаемым местам; это задача поставщика ADO.NET, чтобы использовать его, но; он работает в большинстве случаев. Обратите внимание, что это может привести к SqlException, а не к OperationCancelledException, если операция выполняется; это снова относится к провайдеру ADO.NET, но имеет большой смысл: вы могли бы прервать что-то важное; он возникает как критическая проблема подключения.

Что касается вопросов:

Почему весь фрагмент полностью строится, предполагая, что ошибка компилятора для всего решения отсутствует?

Потому что... это действительно С#, даже если он не делает то, что вы ожидаете.

Простите меня, поскольку я не могу проверить, вызывает ли вызов tokenSource.Cancel() действительно отмену метод, потому что я не знаю, как генерировать длинный SQL-запрос. Удалит ли .Cancel() этот метод и выбрасывает OperationCancelledException?

Спецификатор ADO.NET, но он обычно работает. В качестве примера "как создать длинный SQL-запрос"; команда waitfor delay на сервере SQL здесь несколько полезна, и это то, что я использую в тестах интеграции.

Ответ 2

Вы можете исправить SqlMapper.cs в Dapper lib добавив следующие строки:

    internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> paramReader)
    {
        var cmd = cnn.CreateCommand();

#if ASYNC
        // We will cancel our IDbCommand
        CancellationToken.Register(() => cmd.Cancel());
#endif

Восстановите свой собственный Dapper lib и наслаждайтесь:)

Ответ 3

попробуйте использовать SqlConnection и поймать исключение при отмене

var sqlConn = db.Database.Connection as SqlConnection;
sqlConn.Open();

_cmd = new SqlCommand(textCommand, sqlConn);
_cmd.ExecuteNonQuery();

и отмените SqlCommand

_cmd.Cancel();

Ответ 4

Я использовал один SqlConnection для нескольких потоков. И затем, когда я изменил его так, чтобы каждый Thread создал свой собственный SqlConnection ошибка исчезла.