c# – Asynchronous method invocation in WPF

Question:

I have a WPF application working with a database. The number of records in the database is quite large and is constantly growing. The project uses ORM (EF 6). There is a certain class that works directly with the database context:

public class Store : IStore {...}

A number of methods are defined in the interface of the class, the code in which refers directly to the base through the EF context, respectively, the execution of the method takes quite a long time, which can cause a simple user interface, if I understand everything correctly. Therefore, it is necessary to move the operations of receiving data from the database into separate streams. So, let's say the Store defines a method:

public ICollection<Product> GetAllProducts() {...}

If I understand correctly, I need to add its asynchronous implementation:

public async Task<ICollection<Product>> GetAllProductsAsync()
{
    return await Task<ICollection<Product>>.Factory.StartNew(GetAllProducts)
}

How to properly use such a method in the application itself in the ViewModel , Let's say that while the data is loading, the StatusBar displays the progress of loading the data, and the DataGrid displays the result as soon as the data is loaded?

Answer:

Okay, let's start with Task.Factory.StartNew . This is necessary only if your database query does not support asynchrony itself, and requires a separate thread to be allocated (by the way, it is better to write just Task.Run ).

For the fresh Entity Framework this is not the case, asynchronous functions are supported correctly, out of the box: Entity Framework tutorial: async query and save .

With async at the database level, you don't need to create separate threads.


With regard to progress, this is worse. EF does not maintain information about the progress of an operation, so you can simply dump the "read" state, and read until you are done.

In the case when / if progress support is implemented, you can use the IProgress<T> interface.


Thus, the code in the VM will look like this:

IsLoading = true;
var localData = await model.LoadDataAsync();
IsLoading = false;
Data = localData;

Most likely, you don't want to lay out the model classes for the View, so you need a wrapper that creates VM objects for your entities:

// в модели
IQueryable<TEntity> GetData();
// в VM
IsLoading = true;
var localData = new List<EntityVM>();
await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity));
IsLoading = false;
Data = localData;

Well, as @Pavel Mayorov rightly points out, you might want to catch exceptions, so you need to try:

IsLoading = true;
var localData = new List<EntityVM>();
try
{
    await model.GetData().ForEachAsync(entity => localData.Add(new EntityVM(entity));
    Data = new ObservableCollection<EntityVM>(localData);
}
catch
{
    IsFailed = true;
    throw;
}
finally
{
    IsLoading = false;
}

Or, if you want the data to appear not all together, but as it is loaded, probably just

    await model.GetData().ForEachAsync(entity => Data.Add(new EntityVM(entity));

(but here I am not sure, since I have never tried it).

Scroll to Top