Лучшие практики для асинхронных вызовов в MVP с WinForms
-
06-07-2019 - |
Вопрос
Я использую шаблон Model-View-Presenter в проекте WinForms, и одна проблема (среди многих), с которой я сталкиваюсь, заключается в том, что форма говорит докладчику сделать что-то, а затем не реагирует, когда докладчик переходит к сделай это. К счастью, в моем проекте у меня нет проблем с асинхронным вызовом всех докладчиков. Вопрос в том, как именно это сделать?
Должен ли каждый вызов докладчика быть включен в создание нового потока? *
new Thread(()=>_presenter.DoSomething()).Start();
Каковы лучшие практики здесь? Что делать, если пользователь нажимает кнопку «Отменить то, что вы делаете»? кнопка? Как мне прервать изящно?
. * Реально, я бы просто использовал прокси на презентере, чтобы сделать это, вместо того, чтобы помещать создание потока в WinForm
Решение
Я могу только утверждать, что думал об этом (до прочтения вашего вопроса;). Сначала я бы подстроил места, где это действительно имеет значение; например, точка доступа к БД. Если есть место, которое не должно быть выполнено в «пользовательском интерфейсе» контекста .microsoft.com / en-us / library / system.threading.synchronizationcontext.current.aspx в потоке пользовательского интерфейса, а затем сравните его позже с контекстом синхронизации не из пользовательского интерфейса), а затем с Debug.BitchAndMoan (). Любые более длительные вычисления (которые «должны» все четко разделяться в своих собственных многообразиях, верно;) должны утверждать это.
Полагаю, вы должны, по крайней мере, сделать тип выполнения функции презентатора настраиваемым через атрибут, которому затем подчиняется прокси. (на тот случай, если вы хотите, чтобы что-то было сделано серийно).
Отмена задачи на самом деле является проблемой докладчика, но у вас должен быть ссылочный объект, который сообщает, что вы хотите остановить. Если вы пойдете по прокси-серверу, вы можете забрать созданные потоки в список задач с помощью IAsyncResult, но все еще остается проблема решить, какой из них должен быть отменен, если одно и то же действие разрешено вызывать несколько раз параллельно. Таким образом, вы должны предоставить заданию подходящее имя для конкретного вызова при запуске; что подразумевает слишком много логики в сторону View - > Докладчик, вероятно, должен попросить View спросить пользователя, с какой из задач следует избавиться.
По моему опыту, это обычно обходится с помощью событий (стиль SCSF). Если бы я делал это с нуля, я бы пошел по доверенному пути, поскольку SCSF причинял столько боли, что я сомневаюсь в здравом уме его дизайнеров.
Другие советы
Обычно я помещаю любое действие, которое (реально) может занять больше секунды или две, в отдельную задачу, например:
public interface ITask
{
void ExecuteTask (ITaskExecutionContext context);
void AfterSuccess(ITaskExecutionContext context);
void AfterFailure(ITaskExecutionContext context);
void AfterAbortion(ITaskExecutionContext context);
}
У меня также есть абстракция для выполнения таких задач:
public interface ITaskExecutor : IDisposable
{
void BeginTask(ITask task);
void TellTaskToStop();
}
Одна из реализаций этого ITaskExecutor
использует BackgroundWorker
:
public class BackgroundTaskExecutor : ITaskExecutor
{
public void BeginTask(ITask task)
{
this.task = task;
worker = new BackgroundWorker ();
worker.DoWork += WorkerDoWork;
worker.RunWorkerCompleted += WorkerRunWorkerCompleted;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync();
}
...
}
Я сильно полагаюсь на внедрение зависимостей и IoC, чтобы связать вещи вместе. Тогда в презентере я просто называю что-то вроде:
GoAndDontReturnUntilYouBringMeALotOfMoneyTask task = new GoAndDontReturnUntilYouBringMeALotOfMoneyTask(parameters);
taskExecutor.BeginTask(task);
Затем кнопки «Отмена / Отмена» подключаются, чтобы сообщить исполнителю / заданию об отмене.
На самом деле это немного сложнее, чем представлено здесь, но это общая идея.
Почему бы не сделать так, чтобы используемый вами шаблон прокси-сервера принимал пару обратных вызовов для возврата результатов или отмены?