Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to catch "Operation cancelled by user" exception

Tags:

c#

ado.net

i have following peace of code:

IAsyncResult beginExecuteReader = command.BeginExecuteNonQuery();

while (!beginExecuteReader.IsCompleted)
{
    if (controllerTask.CancellationTokenSource.IsCancellationRequested)
    {
        command.Cancel();
    }

    Thread.Sleep(100);
}

try
{
    result = command.EndExecuteNonQuery(beginExecuteReader);
}
catch (SqlException exception)
{
    if (exception.ErrorCode == OperationCanceled)
    {
        throw new OperationCanceledException();
    }

    throw;
}

How can i identify, that catched exception is caused by operation cancelation. In this case ExecuteNonQuery throws exception with error code 0x80131904, but it's very general exception which can be caused by many reasons. Error message looks like this: {"A severe error occurred on the current command. The results, if any, should be discarded.\r\nOperation cancelled by user."}

I don't see any options except of parsing of error message... Any ideas?

Thanks

PS. Yeah, i know that Cancel command for asyncronyc operation probably is not the best idea, because for .NET 2.0 there was warning on MSDN, but for .NET 4.0 this warning is removed. And i also don't like another implementations when cancel method is called from another thread, as for me it makes code more difficult

like image 973
and85 Avatar asked Apr 19 '12 10:04

and85


1 Answers

There doesn't seem to be a locale insensitive mechanism to catch just this error. The HResult 0x80131904 is just COR_E_SqlException. The error is initiated at TdsParser.cs:2332 without any unique properties. It is almost the exact same code as :2759 - Unknown Error and :3850 - Unexpected Collation.

Here are the bad solutions I have come up with:

Option 1: Break the good advice of "don't make logic locale sensitive"

using (var con = new SqlConnection("Server=(local);Integrated Security=True;"))
{
    con.Open();

    try
    {
        var sqc = new SqlCommand("WAITFOR DELAY '1:00:00'", con);
        var readThread = Task.Run(() => sqc.ExecuteNonQuery());

        // cancel after 5 seconds
        Thread.Sleep(5000);
        sqc.Cancel();

        // this should throw
        await readThread;

        // unreachable
        Console.WriteLine("Succeeded");
    }
    catch (SqlException ex) when (ex.Number == 0 && ex.State == 0 && ex.Class == 11 
        && ex.Message.Contains("Operation cancelled by user."))
    {
        Console.WriteLine("Cancelled");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error");
    }
}

Option 2: Assume that no other severe locally generated error matters after a cancel has been issued

using (var con = new SqlConnection("Server=(local);Integrated Security=True;"))
{
    con.Open();

    bool isCancelled = false;
    try
    {
        var sqc = new SqlCommand("WAITFOR DELAY '1:00:00'", con);
        var readThread = Task.Run(() => sqc.ExecuteNonQuery());

        // cancel after 5 seconds
        Thread.Sleep(5000);
        isCancelled = true;
        sqc.Cancel();

        // this should throw
        await readThread;

        // unreachable
        Console.WriteLine("Succeeded");
    }
    catch (SqlException ex) when (isCancelled && ex.Number == 0 && ex.State == 0 && ex.Class == 11)
    {
        Console.WriteLine("Cancelled");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error");
    }
}
like image 188
Mitch Avatar answered Nov 03 '22 17:11

Mitch