C # Как CancellationToken используется с SqlConnection.OpenAsync (токен)? - PullRequest
0 голосов
/ 11 января 2019

Я пытаюсь использовать CancellationToken с SqlConnection.OpenAsync (), чтобы ограничить время, которое занимает функция OpenAsync.

Я создаю новый CancellationToken и настраиваю его на отмену, скажем, через 200 миллисекунд. Затем я передаю его OpenAsync (токену). Однако запуск этой функции может занять несколько секунд.

Глядя на документацию, я не могу понять, что я делаю неправильно. https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.openasync?view=netframework-4.7.2

Вот код, который я использую:

    private async void btnTestConnection_Click(object sender, EventArgs e)
    {
        SqlConnection connection = new SqlConnection(SQLConnectionString);
        Task.Run(() => QuickConnectionTest(connection)).Wait();
    }

    public async Task QuickConnectionTest(SqlConnection connection)
    {
        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        source.CancelAfter(200);

        ConnectionOK = false;

        try
        {
            using (connection)
            {
                await connection.OpenAsync(token);

                if (connection.State == System.Data.ConnectionState.Open)
                {
                    ConnectionOK = true;
                }

            }
        }
        catch (Exception ex)
        {
            ErrorMessage = ex.ToString();
        }
    }

Я ожидал, что OpenAsync () завершится рано, когда CancellationToken сгенерирует исключение OperationCanceledException, когда прошло 200 мс, но оно просто ждет.

Чтобы повторить это, я делаю следующее:

Запустите код: Результат = Соединение в норме Остановите службу SQL Запустите код: зависает на длину соединения. Время ожидания

Ответы [ 2 ]

0 голосов
/ 14 января 2019

CancelationToken работает для всех остальных так же, как и для вас в вашем собственном коде.
Пример:

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("main");
        var cts = new CancellationTokenSource();
        var task = SomethingAsync(cts.Token);
        cts.Cancel();
        await task;
        Console.WriteLine("Complete");
        Console.ReadKey();
    }

    static async Task SomethingAsync(CancellationToken token)
    {
        Console.WriteLine("Started");
        while (!token.IsCancellationRequested)
        {
            await Task.Delay(2000); //didn't pass token here because we want to simulate some work.
        }
        Console.WriteLine("Canceled");
    }
}

//**Outputs:**
//main
//Started
//… then ~2 seconds later <- this isn't output
//Canceled
//Complete

Метод OpenAsync может потребовать некоторой оптимизации, но не ожидайте, что вызов Cancel() немедленно отменит любую Task. Это просто маршаллированный флаг, позволяющий единице работы в пределах Task знать, что вызывающая сторона хочет отменить ее. Работа внутри Task позволяет выбрать, как и когда отменить. Если он установлен, когда вы устанавливаете этот флаг, тогда вам просто нужно подождать и поверить, что Task делает то, что нужно, чтобы закончить.

0 голосов
/ 14 января 2019

Ваш код мне кажется правильным. Если это не выполняет отмену, как ожидалось, то SqlConnection.OpenAsync не поддерживает надежную отмену. Отмена является кооперативным. Если это не поддерживается должным образом, или если есть ошибка, у вас нет никаких гарантий.

Попробуйте выполнить обновление до последней версии .NET Framework. Вы также можете попробовать .NET Core, который, похоже, возглавляет .NET Framework. Я следую за репозиториями GitHub и вижу множество ошибок ADO.NET, связанных с асинхронностью.

Вы можете попробовать позвонить SqlConnection.Dispose() после истечения времени ожидания. Может быть, это работает. Вы также можете попробовать использовать синхронный API (Open). Может быть, это использует другой путь кода. Я считаю, что это не так, но стоит попробовать.

В случае, если это также не работает, вам нужно написать свой код, чтобы ваша логика продолжалась, даже если задача подключения не была выполнена. Притворись, что он завершен, и пусть он задержится на заднем плане, пока не завершится сам Это может привести к увеличению использования ресурсов, но это может быть хорошо.

В любом случае рассмотрите возможность открытия проблемы в репозитории corefx GitHub с минимальным воспроизведением (например, 5 строк, соединяющихся с example.com). Это должно просто работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...