C# Выполняйте один и тот же запрос несколько раз, чтобы проверить, не возвращается ли что-нибудь

#c# #sql #sql-server

Вопрос:

У меня есть SQL-запрос, который я хочу проверить, если что-то вернется. Цель состоит в том, чтобы ничего не вернуть, и я хочу повторить попытку в течение 15 секунд, если что-то будет возвращено. Однако мне трудно заставить этот цикл работать должным образом. Может ли кто-нибудь предложить исправить этот код? Он переходит к «SQL-таблице хорошо!» после 2 попыток, хотя запрос должен был что-то вернуть.

 //SqlDataReader readerPromo = promoTablecmd.ExecuteReader();
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
//Loop so that if less than 10 seconds, keep retrying SQL cmd above.
while (true)
{
    double elapsedTime = stopwatch.ElapsedMilliseconds;
    SqlDataReader readerPromo = promoTablecmd.ExecuteReader();
    // If query returned something
    if (readerPromo.Read())
    {


        if (elapsedTime > 15000)
        {
            Console.WriteLine("Error found!");
            break;
        }
        else
        {
            Console.WriteLine("Found descrepency in SQL table retrying again for 10 seconds...");
            System.Threading.Thread.Sleep(1000);
            continue;
        }

    }
    else {

         Console.WriteLine("SQL table good!");
    }
}
 

Комментарии:

1. Переместитесь execute reader в петлю

2. Вы должны повторно выполнять запрос каждый раз, когда хотите проверить. Новые результаты не передаются автоматически в открытый читатель. Таким образом, первая строка кода должна находиться внутри цикла, так как вы хотите выполнять ее на каждой итерации цикла.

3. Я попробовал, однако он продолжает выдавать мне, что с этой командой уже связан открытый читатель данных, который сначала должен быть закрыт. Я попытался установить «MultipleActiveResultSets=true» в моем соединении, но это не сработало. Пожалуйста, смотрите отредактированный код.

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

5. promoTablecmd.ExecuteScalar() это полностью избавило бы от необходимости в читателе. Все, что вы хотите знать здесь, это есть ли хотя бы один результат, вам не нужен читатель

Ответ №1:

Использование асинхронности здесь более корректно. Это должно сработать:

     [Test]
    public async Task Repro()
    {
        var sqlCmd = new SqlCommand();//mock
        using var cts = new CancellationTokenSource();
        cts.CancelAfter(TimeSpan.FromSeconds(15));//timeout after 15 seconds
        await WaitEndOfDataAsync(sqlCmd, cts.Token, TimeSpan.FromSeconds(1));//setting check interval
    }

    private static async Task WaitEndOfDataAsync(SqlCommand cmd, CancellationToken cancellationToken, TimeSpan checkInterval)
    {
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            using(var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))
            {
                var hasData = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);
                await reader.CloseAsync().ConfigureAwait(false);

                if (!hasData)
                    return;
            }
            cancellationToken.ThrowIfCancellationRequested();
            await Task.Delay(checkInterval, cancellationToken);
        }
    }
 

Он будет перебирать данные, и если истечет время ожидания — вызовет исключение OperationCancelledException.