Токен отмены на 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
ошибка исчезла.