I have a view model with 2 collections, that takes the data from database. For example countries and type of VAT.
I would like to defer the initialization of this collections until I will, becauase if I am not wrong, it is better don't do it in the constructor becuase it is a high cost resource and the constrcutor has to be cheap to create.
So one option that I am trying to use is lazy initialization. this is the documentation.
But I am using a little bit different code, because the methods that get the data from the database are async.
This is my view model:
public class MyViewModel
{
//Service to get data from database.
private readonly IDataService _dataService;
//The service is injected with dependency injection.
public MyViewModel(IDataService paramDataService)
{
_dataService = paramDataService;
_countriesoOc = new Lazy<Task<ObservableCollection<Contry>>>(InitializeContriesAsync);
_vatsoOc = new Lazy<Task<ObservableCollection<VAT>>>(InitializeVatsAsync);
}
private readonly Lazy<Task<ObservableCollection<Country>>> _countriesOc;
public IReadOnlyCollection<Cauntry> Coutries => _countriesOc.Value.Result;
public Country? SelectedCountry;
private async Task<ObservableCollection<MagnitudDTO>> InitializeCountriesAsync()
{
//Tset long process
await Task.Delay(5000).ConfigureAwait(false);
return new ObservableCollection<Contry>(await _dataService.GetAllContriesAsync().ConfigureAwait(false));
}
private readonly Lazy<Task<ObservableCollection<VAT>>> _vatsOc;
public IReadOnlyCollection<VAT> Vats => _vatsOc.Value.Result;
public VAT? SelectedVat;
private async Task<ObservableCollection<VAT>> InitializeVatsAsync()
{
//Tset long process
await Task.Delay(5000).ConfigureAwait(false);
return new ObservableCollection<VAT>(await _dataService.GetAllVatsAsync().ConfigureAwait(false));
}
public void SetInvoce(Invoce paramInvoce)
{
SelectedCountry = _countriesOc.FirstOrDefault(x => x.Id = paramInvoce.IdCountry);
SelectedVat = _vatsOc.FirstOrDefault(x => x.Id = paramInvoce.IdVat);
}
}
I have realize that in this case, when I set the first invoice, it takes 10 seconds, because it seems when it set the country it waits the 5 seconds of the initialization and then continue with the set of the VAT, that takes another 5 seconds.
There is some way to set the selected items in a parallel way? I guess that perhaps I am doing wrong using the .Result property in the collections, but I am not sure.
I would try another option, that is using an intilize method that run all the methods in parallel. This is the solution:
public class MyViewModel
{
//Service to get data from database.
private readonly IDataService _dataService;
//The service is injected with dependency injection.
public MyViewModel(IDataService paramDataService)
{
_dataService = paramDataService;
}
public Task InitializeAsync()
{
return Task.WhenAll(IntializeCountriesAsync(), InitializeVatsAsync());
}
private readonly ObservableCollection<Country> _countriesOc;
public IReadOnlyCollection<Cauntry> Coutries => _countriesOc;
public Country? SelectedCountry;
private async Task<ObservableCollection<MagnitudDTO>> InitializeCountriesAsync()
{
//Tset long process
await Task.Delay(5000).ConfigureAwait(true);
_countriesOc.AddRange(await _dataService.GetAllCountriesAsync().ConfigureAwait(true));
}
private readonly Lazy<Task<ObservableCollection<VAT>>> _vatsOc;
public IReadOnlyCollection<VAT> Vats => _vatsOc.Value.Result;
public VAT? SelectedVat;
private async Task<ObservableCollection<VAT>> InitializeVatsAsync()
{
//Tset long process
await Task.Delay(5000).ConfigureAwait(true);
_vatsOc.AddRange(await _dataService.GetAllVatsAsync().ConfigureAwait(true));
}
public void SetInvoce(Invoce paramInvoce)
{
SelectedCountry = _countriesOc.FirstOrDefault(x => x.Id = paramInvoce.IdCountry);
SelectedVat = _vatsOc.FirstOrDefault(x => x.Id = paramInvoce.IdVat);
}
}
In this case, each object that use the view model, get the instance from dependency injection, but it has to call to the initializeAsync() method to populate the collections.
The advange of this solution it is that the initialization takes 5 seconds, because all the collections are initialize in parallel. But I don't like that each object that consume the view model, has to initialize it. It is less transparent for the consumer that the async lazy solution, but it is much faster. If I have many collections, they are initialized in parallel.
So in sumary, I would like to know if there is some way to improve the async lazy solution to run the initialization in parallel, if not, I guess i would go for the second solution, although each consumer has to call the initalize method always before can use it.
Thanks.
SetInvoceto beasync Taskand await tasks every time.InitializeAsyncmethod which are called from the view when the view is ready for presentation (e.g. WPF inside Loaded event of the Window). sample on github