@romaro

В чем может быть причина бага при асинхронной загрузке формы?

Баг проявляется не всегда и, видимо, зависит от времени выполнения асинхронной операции. В обычном рантайме программа просто зависает, при дебаге удается получить только стандартное сообщение о доступе из другого потока. Демонстрация на видео: https://youtu.be/DdEzrM86MJk

Форма создается следующим образом. Вначале получаю объект вью-модели, который назначаю в качестве DataContext, затем показываю форму:
public override void ShowDataRecordForm(DataDomainMetadata md, int id)
        {
            var obj = new DataDomainObject(md, id);
            DataRecordContext ctx = new(obj);
            var form = GetForm(ctx);
            form.Owner = App.MainFormStatic;
            form.StartPosition = FormStartPosition.CenterScreen;
            form.Show();
        }


В конструкторе вью-модели запускается таск:
public DataRecordContext(DataDomainMetadata md, int id)
            : this(md)
        {
            DataLoadStatus = DataLoadStatusEnum.NotLoaded;
            Task.Factory.StartNew(() => RunDataLoadAsync(id, _cts.Token), TaskCreationOptions.LongRunning);
            //Task.Run(() => RunDataLoadAsync(id, _cts.Token));
        }


Сама асинхронная операция выглядит так:
async Task RunDataLoadAsync(int id, CancellationToken ct)
        {
            try
            {
                OnDataLoadStatusChanged(DataLoadStatusEnum.OnLoading);

                var row = await Provider.GetDataRecordRowAsync(id);

                if (row == null)
                    return;

                if (ct.IsCancellationRequested)
                    return;

                SetDataRow(row);
            }
            catch
            {
                OnDataLoadStatusChanged(DataLoadStatusEnum.Error);
            }
        }


Когда форма получает контекст (вью-модель), все дочерние контролы обрабатывают событие ContextChanged:
protected override void OnDataContextChanged(EventArgs e)
        {
            base.OnDataContextChanged(e);

           
      if (DataContext is IAsyncViewModel asyncViewModel)
                {
                    //Эта задержка скрывает проблему, но не решает ее
                    //await Task.Delay(100);

                    if (asyncViewModel.IsDataLoaded)
                    {
                        BindValue();
                    }
                    else
                    {
                        SetLoading();
                    }

                    asyncViewModel.DataLoadStatusChanged += DataContext_DataLoadStatusChanged;
                }
        }


Если на момент загрузки строкового контрола вью-модель готова (IsDataLoaded == true), сразу выполняется биндинг. Если нет, биндинг будет обработан в подписке. Но поскольку делегат будет вызываться из RunDataLoadAsync, который в другом потоке, выполняю проверку InvokeRequired:

void DataContext_DataLoadStatusChanged(object? sender, DataLoadStatusChangedEventArgs e)
        {
            void action()
            {
                if (DataContext is IAsyncViewModel)
                {
                    switch (e.Status)
                    {
                        case DataLoadStatusEnum.OnLoading:
                            {
                                SetLoading();
                                break;
                            }
                        case DataLoadStatusEnum.Loaded:
                            {
                                BindValue();
                                SetLoaded();
                                break;
                            }
                        case DataLoadStatusEnum.Error:
                            {
                                SetError();
                                break;
                            }
                    }
                }
            }

            if (InvokeRequired)
            {
                Invoke(() => action());
            }
            else
            {
                action();
            }
        }


Я попробовал воспроизвести этот баг на тестовом проекте (github), но там она себя не проявляет.

P.S. Знаю, что у этого кода есть проблема с тем, что между проверкой на IsDataLoaded и подпиской на событие существует лаг, за который RunDataLoadAsync может вызвать OnDataLoadStatusChanged и контрол никогда не выйдет из статуса загрузки, но вряд ли это относится к проблеме.
  • Вопрос задан
  • 325 просмотров
Решения вопроса 1
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы