CancellationToken with async Dapper methods?

为君一笑 提交于 2019-12-31 17:51:13

问题


I'm using Dapper 1.31 from Nuget. I have this very simple code snippet,

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();
}

When I press F12 on QueryAsync, it points me to

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

There is no CancellationToken on its signature.

Questions:

  • Why is the snippet completely buildable assuming that there is no compiler error on the whole solution?
  • Forgive me as I cannot test if calling tokenSource.Cancel() would really cancel the method because I don't know how to generate long running sql query. Will the .Cancel() really cancels the method and throws OperationCancelledException?

Thank you!


回答1:


You are passing the cancellation token as the parameter object; that won't work.

The first async methods in dapper did not expose a cancellation token; when I tried to add them as an optional parameter (as a separate overload, to avoid breaking existing assemblies), things got very confused with "ambiguous method" compilation problems. Consequently, I had to expose this via a separate API; enter CommandDefinition:

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

This then passes the cancellation-token down the chain to all the expected places; it is the job of the ADO.NET provider to actually use it, but; it seems to work in most cases. Note that it can result in a SqlException rather than an OperationCancelledException if the operation is in progress; this again is down to the ADO.NET provider, but makes a lot of sense: you could have interrupted something important; it surfaces as a critical connection issue.

As for the questions:

Why is the snippet completely buildable assuming that there is no compiler error on the whole solution?

Because... it is valid C#, even if it doesn't do what you expect.

Forgive me as I cannot test if calling tokenSource.Cancel() would really cancel the method because I don't know how to generate long running sql query. Will the .Cancel() really cancels the method and throws OperationCancelledException?

ADO.NET provider-specific, but yes it usually works. As an example of "how to generate long running sql query"; the waitfor delay command on SQL server is somewhat useful here, and is what I use in the integration tests.




回答2:


You can fix SqlMapper.cs in Dapper lib by adding these lines:

    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

Rebuild your own Dapper lib and enjoy :)




回答3:


try using a SqlConnection and catch the exception on cancel

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

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

and cancel the SqlCommand

_cmd.Cancel();



回答4:


I was using one SqlConnection for multiple threads. And then when I changed it so that each Thread created it's own SqlConnection the error disappeared.



来源:https://stackoverflow.com/questions/25540793/cancellationtoken-with-async-dapper-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!