我是否以错误的方式使用异步延迟初始化?

问题描述 投票:0回答:1

我有一个包含 2 个集合的视图模型,它从数据库中获取数据。例如国家和增值税类型。

我想推迟这个集合的初始化,直到我愿意为止,因为如果我没有错的话,最好不要在构造函数中这样做,因为它是一种高成本资源,并且构造函数的创建成本必须很低。

所以我尝试使用的一个选项是延迟初始化。 这是文档

但是我使用的代码有点不同,因为从数据库获取数据的方法是异步的。

这是我的视图模型:

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);
    }
}

我意识到,在这种情况下,当我设置第一张发票时,需要 10 秒,因为似乎在设置国家/地区时,它等待初始化的 5 秒,然后继续设置增值税,这需要另一个时间5秒。

有什么方法可以并行设置所选项目吗?我想也许我在集合中使用 .Result 属性做错了,但我不确定。

我会尝试另一种选择,即使用并行运行所有方法的 intilize 方法。这就是解决方案:

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);
    }
}

在这种情况下,每个使用视图模型的对象都会从依赖注入中获取实例,但它必须调用initializeAsync()方法来填充集合。

这个解决方案的优点是初始化需要 5 秒,因为所有集合都是并行初始化的。但我不喜欢每个使用视图模型的对象都必须初始化它。对于消费者来说,异步惰性解决方案的透明度较低,但速度要快得多。如果我有很多集合,它们会并行初始化。

所以总而言之,我想知道是否有某种方法可以改进异步惰性解决方案以并行运行初始化,如果没有,我想我会选择第二种解决方案,尽管每个消费者都必须调用 initalize 方法之前总是可以使用它。

谢谢。

c# lazy-initialization
1个回答
0
投票

第二个选项可以重写为:

public class MyViewModel
{
    private readonly IDataService _dataService;
    private readonly Lazy<Task> InitTask;

    public MyViewModel(IDataService paramDataService)
    {
        _dataService = paramDataService;
        InitTask = new Lazy<Task>(Task.WhenAll(IntializeCountriesAsync(), InitializeVatsAsync()));
    }

    // ...
    public void SetInvoce(Invoce paramInvoce)
    {
        InitTask.Value.Wait();
        SelectedCountry = _countriesOc.FirstOrDefault(x => x.Id = paramInvoce.IdCountry);
        SelectedVat = _vatsOc.FirstOrDefault(x => x.Id = paramInvoce.IdVat);
    }
}

虽然我个人强烈建议遵循不要阻塞异步代码的建议,并且也使

SetInvoce
异步:

public async Task SetInvoce(Invoce paramInvoce)
{
    await InitTask.Value.ConfigureAwait(false);
    SelectedCountry = _countriesOc.FirstOrDefault(x => x.Id = paramInvoce.IdCountry);
    SelectedVat = _vatsOc.FirstOrDefault(x => x.Id = paramInvoce.IdVat);
}

如果这是一个选项,那么我会放弃第一种情况的“同步包装器”,然后执行以下操作:

public async Task SetInvoce(Invoce paramInvoce)
{
    await Task.WhenAll(_countriesoOc, _vatsoOc).ConfigureAwait(false);
    SelectedCountry = _countriesoOc.Result.FirstOrDefault(x => x.Id = paramInvoce.IdCountry);
    SelectedVat = _vatsOc.Result.FirstOrDefault(x => x.Id = paramInvoce.IdVat);
}
© www.soinside.com 2019 - 2024. All rights reserved.