Транзакции / подсказка к 2 задачке

Добавляем на форму панельку куда будем выводить статистику, и делаем на ней лейбл lblStatistics чтобы просто в него писать данные

Добавляем timer на форму

и ставим ему свойства какие-то такие

Enabled – это чтобы таймер запустился. А интервал — это как часто мы будем опрашивать базу.

Теперь тыкаем два раза на таймер.

Добавим теперь метод для расчета статистики. Делаем его по аналогии с методами для генерации. То есть он должен принимать на вход конекшен.

// метод для генерации статистики
private void GetStatistics(SqlConnection connection)
{
    using (connection)
    {
        connection.Open();
    }
}

// метод вызывающийся каждую итерацию таймера, не забываем добавить async
private async void timer1_Tick(object sender, EventArgs e)
{
    var connection = GetConnection();
    await Task.Run(() =>
    {
        GetStatistics(connection);
    });
}

Теперь мне надо сделать чтобы метод собственно эту статистику возвращал. Так как статистика может содержать кучу данных. То создадим под нее отдельный класс.

я хочу, чтобы мой метод который GetStatistics, возвращал экземпляр этого класса. А реакция на тик таймера, которая запускает этот метод асинхронно получала этот экземпляр и выводила его на форму. К счастью тут почти не надо править код. Достаточно просто написать код — вот так

// тут меняем void на Statistics
private Statistics GetStatistics(SqlConnection connection)
{
    var statistics = new Statistics();

    using (connection)
    {
        connection.Open();
    }

    return statistics;
}

private async void timer1_Tick(object sender, EventArgs e)
{
    var connection = GetConnection();
    // тут сохраняем результат асинхронного вызова в переменную
    Statistics statistics = await Task.Run(() =>
    {
        // ну и тут return добавляем
        return GetStatistics(connection);
    });
}

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

private Statistics GetStatistics(SqlConnection connection)
{
    var statistics = new Statistics();

    using (connection)
    {
        connection.Open();
        
        // запрашиваем количество кошек
        var command = new SqlCommand("SELECT count(*) FROM cats", connection);
        statistics.catsCount = (int)command.ExecuteScalar(); // сохраняем в статистку
        
        // запрашиваем количество кошек по породам
        command = new SqlCommand("SELECT breed, count(*) as count FROM cats GROUP BY breed", connection);
        // создаем экземпляр словарика
        statistics.catsCountsByBreed = new Dictionary<string, int>();
        // ну и проходим по строкам которые вернул запрос
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                if (!reader.IsDBNull("breed")) // проверяем что порода не пустая
                {
                    // добавляем в словарик запись, в качестве ключа название породы, в качестве значение количество
                    statistics.catsCountsByBreed[reader.GetString("breed")] = reader.GetInt32("count");
                }
            }
        }
    }

    return statistics;
}

private async void timer1_Tick(object sender, EventArgs e)
{
    var connection = GetConnection();
    var statistics = await Task.Run(() =>
    {
        return GetStatistics(connection);
    });
    
    // тут хитро формируем строку со списком пород
    var breedsInfo = String.Join(
        "\n",
        statistics.catsCountsByBreed
            .Select(x => x.Key)
            .OrderBy(x => x)
            .Select(x => $" - {x}: {statistics.catsCountsByBreed[x]}")
    );
    
    // и теперь выводим на форму
    lblStatistics.Text = $"Кошек и котов: {statistics.catsCount}\n" 
        + $"{breedsInfo}";
}

запускаем и проверяем:

красота! =)

2

Добавить асинхронный вывод статистки по данным в БД которая будет обновляться по таймеру.