Async/await в DataLayer - потерял преимущества

152
13 февраля 2019, 04:40

В небольшом приложении получаю данные из SQL во ViewModel напрямую через SqlCommand.ExecuteReaderAsync() и SqlDataReader.ReadAsync(). Полученная коллекция Collection<T> отображается в DataGrid. Запрос не быстрый, но такая связка позволяет не блокировать UI, и DataGrid заполняется по мере поступления данных из SQL, что хорошо.

Решил перенести запрос в DataLayer, создал async-метод GetCollectionAsync(). UI по прежнему не блокируется, но теперь DataGrid заполняется только после получения всей коллекции. Получается, что я выиграл от разделения кода на уровни, но проиграл в функциональности. Что я делаю не так?

Было:

public class ViewModel
{
        public ICollection<Order> colOrders;
        public async void GetDataAsync()
        {
        using (SqlConnection sqlConnect = new qlConnection(connectionString))
        {
            SqlCommand cmd = new SqlCommand("select Id, Customer from Orders", sqlConnect);
            sqlConnect.Open();
            SqlDataReader reader = await cmd.ExecuteReaderAsync();
            if (reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    colOrders.Add(new Order(reader["Id"], reader["Customer"]));
                }
            }
            reader.Close();
        }
    }
}

Стало:

public class ViewModel
{
    public ICollection<Order> colOrders;
    protected async void GetDataAsync() {
        DataLayer dl = new DataLayer();
        colOrders = await dl.GetDataAsync();
    }
}

Код в Task<ICollection<Order>>DataLayer.GetDataAsync() делает всё то же самое, что и предыдущий. Всякие try...catch для простоты опускаю.

Answer 1

Можете сделать примерно так. С обычными IEnumerable будет сложно, а здесь вы просто подписываетесь на событие, которое запускается при добавлении новых записей:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    var collection = new ObservableCollection<string>();
    collection.CollectionChanged += (s, args) =>
    {
        if (args.Action == NotifyCollectionChangedAction.Add)
            foreach (string value in args.NewItems)
                listbox.Items.Add(value);
    };
    await GetData(collection);
}
private async Task GetData(ObservableCollection<string> collection)
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(1000);
        collection.Add("Item-" + i);
    }
}
Answer 2

Нашел устраивающий меня вариант решения (спасибо Zergatul, натолкнул на идею).

Вместо

protected async void GetDataAsync() {
    DataLayer dl = new DataLayer();
    colOrders = await dl.GetDataAsync();
}

стал передавать коллекцию как параметр в DataLayer, а выход поменял на bool. Получилось:

protected async void GetDataAsync() {
    DataLayer dl = new DataLayer();
    bool isOK = await dl.FillDataAsync(colOrders);
}

UI не блокируется, коллекция к моменту вызова DataLayer у меня уже есть, данные вываливаются в DataGrid порциями - всё как я хотел.

READ ALSO
Аргументы метода LINQ select

Аргументы метода LINQ select

Не понимаю, почему такЕсть вызов асинхронного метода

147
DataRowExtensions.SetField vs индексатор

DataRowExtensions.SetField vs индексатор

Есть ли какая-то разница между присваиванием нового значение DataRow через DataRowExtensionsSetField и через индексатор по имени?

123
Как указывать action у вебформы в asp.net

Как указывать action у вебформы в asp.net

У меня возникла проблема с элементарной отправкой post запросаПока я гуглил, еще больше запутался

122
Как это можно оптимизировать?

Как это можно оптимизировать?

Как это можно оптимизировать и привести в нормальный вид?

149