Синхронизировать задачу asyn c в WinForms-Validating-Event - PullRequest
1 голос
/ 06 августа 2020

У меня есть приложение classi c WinForms, которое обменивается данными asyn c с серверным API. Это реализуется шаблоном asyn c await.

Теперь у меня проблема с событием «Validating». Мы используем это событие для проверки на стороне клиента (проверяем, является ли ввод нулевым) и устанавливаем отмену. Если ввод действителен, я отправляю его на сервер asyn c и жду результата.

Это моя проблема. Теперь контроль означает, что он действителен. Когда обратный вызов устанавливает для события отмены значение false, уже слишком поздно.

Вот мой код:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public string CustomerName { get; set; }

    private async void Button1_Validating(object sender, CancelEventArgs e)
    {
        if (CustomerName == null)
        {
            e.Cancel = true;
        }
        else
        {
            var isValidAndSaved = await SaveOnServerAsync(CustomerName);

            // Here is the Problem: Setting e.Cancel in callback is too late.
            if (isValidAndSaved)
            {
                e.Cancel = false;
            }
            else
            {
                e.Cancel = true;
            }
        }
    }

    /// <returns>True if the given model was valid and save operation was successfull.</returns>
    public async Task<bool> SaveOnServerAsync(string customerName)
    {
        await Task.Delay(3000); // Send given customerName to server in real app.
        return customerName.Contains("%") ? false : true;
    }
}

Я пробовал программировать classi c (синхронно) для этого случая с помощью 'Подождите 'Таксы в основном потоке. Но затем я сгенерировал тупик.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

        public string CustomerName { get; set; } = "3";

    private void Button1_Validating(object sender, CancelEventArgs e)
    {
        if (CustomerName == null)
        {
            e.Cancel = true;
        }
        else
        {
            // Deadlock because MainThread waits for callback. 
            var isValidAndSaved = SaveOnServerAsync(CustomerName).Result;
            if (isValidAndSaved)
            {
                e.Cancel = false;
            }
            else
            {
                e.Cancel = true;
            }
        }
    }

    /// <returns>True if the given model was valid and save operation was successfull.</returns>
    public async Task<bool> SaveOnServerAsync(string customerName)
    {
        await Task.Delay(3000); // Send given customerName to server in real app.
        return customerName.Contains("%") ? false : true;
    }
}

Есть ли у кого-нибудь идея для хорошего шаблона?

Если я установлю Cancel в false перед ожиданием задачи asyn c, тогда моя проблема будет что пользовательский щелчок отбрасывается и пользовательский опыт достаточен.

1 Ответ

0 голосов
/ 11 августа 2020

Нашел рабочий способ. Для всех остальных задач, кроме первой задачи, требуется " ConfigureAwait (false) ". См. Метод ' SaveOnServerAsyn c'.

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public string CustomerName { get; set; } = "3";

        private void Button1_Validating(object sender, CancelEventArgs e)
        {
            if (CustomerName == null)
            {
                e.Cancel = true;
            }
            else
            {
                var isValidAndSaved = SaveOnServerAsync(CustomerName).Result;
                if (isValidAndSaved)
                {
                    e.Cancel = false;
                }
                else
                {
                    e.Cancel = true;
                }
            }
        }

        /// <returns>True if the given model was valid and save operation was successfull.</returns>
        public async Task<bool> SaveOnServerAsync(string customerName)
        {
            await Task.Delay(3000)
                // The solution is that all deeper Tasks need line below.
                .ConfigureAwait(false);
            ;
            return customerName.Contains("%") ? false : true;
        }
    }

Я нашел много статей об этой проблеме. Это звучит немного неверно из MS.

Рисунок 3 этой статьи MS объясняет ситуацию взаимоблокировки: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

Решение, позволяющее избежать взаимоблокировок, объясняется здесь: https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#configure -context

...