Токен отмены на 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?

Спасибо!

3 ответа

Решение

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

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

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

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

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

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

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

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

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

Вы можете исправить 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 и наслаждайтесь:)

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

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

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

и отменить SqlCommand

_cmd.Cancel();

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

Другие вопросы по тегам