Luffy1
@Luffy1
Student, Junior .NET programmer, C#, JS, HTML/CSS

Как предотвратить изменение пользователем данных во время выполнения async метода сохранения этих данных?

Я разрабатываю WPF проект. По паттерну Репозиторий у меня есть два репозитория, чья имплементация уже готова. Они не используют асинхронность, однако я хочу её добавить. И меня вот что смущает: во время того, как пользователь нажмёт кнопку "Сохранить", что, например, запустит асинхронность, пользователь во время сохранения данных может попробовать изменить данные, например, в ячейках, в котороых он их вводил. Подумал, что CuncurrencyBag решит все проблемы. Написал небольшой тест с ним:
Person person = new Person();
person.Name = "Denis";

var list = new ConcurrentBag<Person>();
list.Add(person);

var task = ChangeVariableAsync(list); // задача начинает выполняться
var el = list.Last(p => p.Name == "Denis");
el.Name = "Dmitry";
Console.WriteLine($"New name: {el.Name}");

await task; // ожидаем завершения задачи

// определение асинхронного метода
async Task ChangeVariableAsync(ConcurrentBag<Person> people)
{
    await Task.Delay(5000);
    try
    {
        var esl = list.Last(p => p.Name == "Denis");
        esl.Name += "s";
        Console.WriteLine($"Old name: {esl.Name}");
    }
    catch (Exception)
    {
        var esl = list.Last(p => p.Name == "Dmitry");
        Console.WriteLine($"Old name: {esl.Name}");
    }
}



class Person
{
    public string Name { get; set; }
}


Однако данные всё равно изменились.
Как можно запретить пользователю изменять данные во время работы асинхронного метода, чтоб при этом не потерялся смысл асинхронности? (бо мы ж её добавляем как раз для того, чтоб основной поток не блокировался).

Или как можно сделать так, чтоб изменения, которые пытается сделать пользователь, не повлияли на сохраняемые данные?

У меня только что появилась идея, мол, что если, пока будет сохранение данных, то сделать ползунок, который показывает, сколько осталось времени? Или сделать что-то наподобие такого. Что скажете?

Или какое решение тут стоит применить?
  • Вопрос задан
  • 99 просмотров
Решения вопроса 3
petermzg
@petermzg
Самый лучший программист
Данные погут меняться только от действий пользователя. Так что включите режим read only пока идет сохранение и никто ничего поменять не сможет
Ответ написан
Комментировать
@mvv-rus
Настоящий админ AD и ненастоящий программист
Простейший вариант: копируйте (синхронно) в обработчике нажатия кнопки данные, которые вы сохраняете, в другое место, а там пользователь пусть хоть обизменяется: на сохраняемые данные это уже не повлияет. Если данных вдруг много, то тогда уже придется синхронизировать доступ к ним (для асинхронной синхронизации подходит SemaphoreSlim.WaitAsync/Release, а если вы можете позволить себе блокировку потока UI, то и lock сойдет). Есть и более сложные схемы - копировать только те данные, которые пользователь собрался изменить, и сохранять в таком случае именно их, а не данные из ViewModel ("копирование при записи"). Короче, дерзайте: вариантов много.
Ну, а вариант с ползунком сам по себе ненадежен: пользователю доверять нельзя. Но как подспорье, вместе с блокировкой изменения - годится.
Ответ написан
Комментировать
grantur5707
@grantur5707
Full Stack Web Developer
Всё зависит от конкретной ситуации...
На мой взгляд, наиболее простым и действенным решением будет временное отключение элементов управления, которые позволяют пользователю менять данные. Отключаете элементы интерфейса пользователю, пока выполняется асинхронный процесс сохранения, и будет вам счастье.

В XAML вы можете сделать что-то вроде этого:
<Button Content="Сохранить" Command="{Binding SaveCommand}" IsEnabled="{Binding IsSaving}" />
<TextBox Text="{Binding Person.Name}" IsEnabled="{Binding IsSaving, Converter={StaticResource InverseBoolConverter}}" />


В ViewModel не забудьте добавить свойство IsSaving, которое будет управлять состоянием элементов:
public bool IsSaving { get; private set; }

private async Task SaveDataAsync()
{
    IsSaving = false;
    RaisePropertyChanged(nameof(IsSaving));

    await repository.SaveAsync(person);

    IsSaving = true;
    RaisePropertyChanged(nameof(IsSaving));
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

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