简体   繁体   中英

cancel async task using cancellationtoken

I would like the users of a web application to be able to cancel long running SQL queries on the server side (by using xhr.abort() method)

I'm using the Response.ClientDisconnectedToken to catch on the server side the event of the canceling of a request by the user (from here: https://stackoverflow.com/a/17713153/8790102 )

The SQL query routine is done in an async method (from here: https://stackoverflow.com/a/24834029/8790102 )

private async Task<DataTable> executeSelectQueryAsync(string SQL, Dictionary<string, object> BindObject = null)
{
    // This cancellationToken is tripped when the client abort the request
    CancellationToken canceltk = HttpContext.Current.Response.ClientDisconnectedToken; // Require IIS 7.5

    DataTable dt = new DataTable();

    // Only line to be logged in the server
    File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "CANCELING ENABLED" + Environment.NewLine);

    try
    {
        dt = await Task.Run(() =>
        {
            // If token is canceled, cancel SQL query
            canceltk.Register(() =>
            {
                File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "CANCELLING..." + Environment.NewLine);
                // scmd is of type OracleCommand
                if (scmd.Connection.State == ConnectionState.Open)
                    scmd.Cancel();
            });

            // Open the connection, execute the SQL command using OracleDataAdapter.Fill method and return a DataTable
            return executeSelectQuery_inner(SQL, BindObject);
        }, canceltk);
    }
    catch (TaskCanceledException ex)
    {
        try
        {
            File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "Cancelling query..." + Environment.NewLine);
            if(scmd.Connection.State == ConnectionState.Open)
                scmd.Cancel();
        }
        catch (Exception ex1)
        {
            File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "Cancel_ERROR:" + ex1.ToString() + Environment.NewLine);
        }
    }
    catch (Exception ex)
    {
        File.AppendAllText(System.Web.HttpContext.Current.Server.MapPath("~/data/logCanceledRequest.txt"), "OTHER EXCEPTION:" + ex.ToString() + Environment.NewLine);
    }

    return dt;
}

My problem is that the method registered in canceltk.Register() is not called when aborting the request (the corresponding text "CANCELLING..." is not logged).

In fact, no text is logged at all. I have no idea why.

If I use a Thread.Sleep(5000) before calling executeSelectQueryAsync and abort the request during these 5 seconds, then the TaskCanceledException is successfully raised and caught.

Task.Run schedules the execution of that code on a thread pool thread which is not controlled by AS.NET's synchronization context. That's why HttpContext.Current is null.

Besides that, in this context, Task.Run causes the execution of the request to be transferred to another thread pool thread, the request handling thread (another thread pool thread) is returned to the pool and when Task.Run finishes execution that thread is returned to the thread pool thread and another one is retrieved and populated with request handling data. You just used more resources for no benefit. Quite the contrary.

Code that escapes the ASP.NET context should not rely on it.

Doesn't the Oracle provider support asynchronous operations?

The method was in fact well called, but System.Web.HttpContext.Current then had a null value, causing a Exception not caught.

By replacing with System.Web.Hosting.HostingEnvironment.MapPath , the code is working successfully.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM