2016-10-04 8 views
0

У меня есть вопрос относительно элегантности и лучшей практики на нижеследующем коде. Концепция приложения:Обновление состояния пользовательского интерфейса, когда все методы Async в разных презентаторах завершены

  1. У меня есть главная форма, которая вызывает пользователь управляет
  2. Это главная форма имеет контроль прогресса, которые показывают пользователю ход нескольких задач с помощью обработчиков событий, которые прикреплены к элементам управления пользователем при необходимости
  3. The View (управление пользователя) «ViewPhase1» имеет событие прогресса меняющегося
  4. Этот взгляд две ведущих
  5. Каждые из этих предъявителей имеют асинхронные задачи
  6. Когда задача собирается начать прогресс изменяется
  7. Когда задача закончилась проверить, если обе задачи были выполнены, и если да прогресс установлен на 100% (сделано)

Потому что у меня есть два разделенных ведущие, которые вызывают методы Async, один из них может завершиться перед другим, поэтому я создал два свойства «DoneTasksWork» в представлении, чтобы иметь возможность в презентаторе, который позволяет узнать, завершены ли задачи каждого презентатора. Если оба варианта завершены, то прогресс в пользовательском интерфейсе устанавливается как 100% (и включает все элементы управления внутри), если прогресс не будет продолжаться по-прежнему.

Это элегантное решение? Думая об этом случае, контроль прогресса только в полном состоянии, когда методы Async в разных классах завершены, могу ли я подойти с другим решением вместо использования логических свойств в качестве флагов?

Спасибо!

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Drawing; 
using System.Data; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows.Forms; 
using Project.Utilities; 

namespace Project 
{ 
    public partial class ViewPhase1 : UserControl, IViewPhase1Work1, IViewPhase1Work2 
    { 
     private PresenterPhase1Work1 _presenterPhase1Work1; 
     private PresenterPhase1Work2 _presenterPhase1Work2; 

     public ViewPhase1() 
     { 
      InitializeComponent(); 
      _presenterPhase1Work1 = new PresenterPhase1Work1(this); 
      _presenterPhase1Work2 = new PresenterPhase1Work2(this); 
     } 

     /// <summary> 
     /// This event is listened by form that invokes this user control and updates progress control. 
     /// </summary> 
     public event ProgressChangedEventHandler ProgressChangedEvent; 
     public void OnProgressHandler(object sender, ProgressChangedEventArgs e) 
     { 
      this.Invoke((Action)delegate 
      { 
       if (this.ProgressChangedEvent != null) 
       { 
        ProgressChangedEvent(this, e); 
       } 
      }); 
     } 
     public void ShowException(string exMessage, MessageBoxButtons bts, MessageBoxIcon icon) 
     { 
      MessageBox.Show("", exMessage, bts, icon); 
     } 
     public DataGridView GridInvoices { get; set; } 
     public DataGridView GridReceipts { get; set; } 
     public DataGridView GridProducts { get; set; } 
     public DataGridView GridCustomers { get; set; } 
     bool IViewPhase1Work1.DoneTasksWork { get; set; } 
     bool IViewPhase1Work2.DoneTasksWork { get; set; } 
    } 

    public interface IViewProgress 
    { 
     event ProgressChangedEventHandler ProgressChangedEvent; 
     void OnProgressHandler(object sender, ProgressChangedEventArgs e); 
    } 

    public interface IViewPhase1Work1 : IViewProgress, IViewException 
    { 
     bool DoneTasksWork { get; set; } 
     DataGridView GridProducts { get; set; } 
     DataGridView GridCustomers { get; set; } 
    } 

    public interface IViewPhase1Work2 : IViewProgress, IViewException 
    { 
     bool DoneTasksWork { get; set; } 
     DataGridView GridInvoices { get; set; } 
     DataGridView GridReceipts { get; set; } 
    } 

    public interface IViewException 
    { 
     void ShowException(string exMessage, MessageBoxButtons bts, MessageBoxIcon icon); 
    } 

    public class PresenterPhase1Work1 
    { 
     private readonly IViewPhase1Work1 _view; 

     public PresenterPhase1Work1(IViewPhase1Work1 view) 
     { 
      _view = view; 
      GetInformation(); 
     } 

     private void GetInformation() 
     { 
      try 
      { 
       var task1 = Task.Run(() => GetDataProducts()); 
       var task2 = Task.Run(() => GetDataCustomers()); 
       Task.WhenAll(task1, task2); 

       _view.GridProducts.DataSource = task1.Result; 
       _view.GridCustomers.DataSource = task2.Result; 
      } 
      catch (Exception ex) 
      { 
       _view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); 
      } 

      finally 
      { 
       _view.DoneTasksWork = true; 
       _view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data")); 
      } 
     } 

     private async Task<object> GetDataCustomers() 
     { 
      return await Util.GetDataCustomerAsync(); 
     } 
     private async Task<object> GetDataProducts() 
     { 
      return await Util.GetDataProductsAsync(); 
     } 
    } 

    public class PresenterPhase1Work2 
    { 
     private readonly IViewPhase1Work2 _view; 

     public PresenterPhase1Work2(IViewPhase1Work2 view) 
     { 
      _view = view; 
      GetInformation(); 
     } 

     private void GetInformation() 
     { 
      try 
      { 
       var task1 = Task.Run(() => GetDataInvoices()); 
       var task2 = Task.Run(() => GetDataReceipts()); 
       Task.WhenAll(task1, task2); 

       _view.GridInvoices.DataSource = task1.Result; 
       _view.GridReceipts.DataSource = task2.Result; 
      } 
      catch (Exception ex) 
      { 
       _view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); 
      } 

      finally 
      { 
       _view.DoneTasksWork = true; 
       _view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data")); 
      } 
     } 

     private async Task<object> GetDataInvoices() 
     { 
      return await Util.GetDataInvoicesAsync(); 
     } 

     private async Task<object> GetDataReceipts() 
     { 
      return await Util.GetDataReceiptsAsync(); 
     } 
    } 
} 

ответ

1

Элегантное решение - нет. Позволь мне объяснить.

Точно, что вы ожидаете от звонка Task.WhenAll(task1, task2); без использования await? Я вам скажу: он проходит прямо и будет блокироваться на _view.GridInvoices.DataSource = task1.Result;, а затем возможен и на следующей строке.

Итак, для того, чтобы решить этот метод (в PresenterPhase1Work1, но это также относится и к другому предъявителю, а):

private void GetInformation() 
    { 
     try 
     { 
      var task1 = Task.Run(() => GetDataProducts()); 
      var task2 = Task.Run(() => GetDataCustomers()); 
      Task.WhenAll(task1, task2); 

      _view.GridProducts.DataSource = task1.Result; 
      _view.GridCustomers.DataSource = task2.Result; 
     } 
     catch (Exception ex) 
     { 
      _view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } 

     finally 
     { 
      _view.DoneTasksWork = true; 
      _view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data")); 
     } 
    } 

может быть изменен на:

private async Task GetInformationAsync() 
    { 
     try 
     { 
      var task1 = Task.Run(() => GetDataProducts()); 
      var task2 = Task.Run(() => GetDataCustomers()); 

      await Task.WhenAny(task1, task2); 
      _view.OnProgressHandler(this, new ProgressChangedEventArgs(50, "Getting data")); 

      await Task.WhenAll(task1, task2); 
      _view.OnProgressHandler(this, new ProgressChangedEventArgs(100, "Done")); 

      _view.GridProducts.DataSource = await task1; 
      _view.GridCustomers.DataSource = await task2; 
     } 
     catch (Exception ex) 
     { 
      _view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error); 
     } 
    } 

Теперь, позвонив асинхронные методы в конструкторе - плохая идея. Конструкторы не позволяют использовать ключевое слово async. Поэтому вы должны перепроектировать свои классы. Поэтому я бы сделал метод GetInformation общедоступным и вызывал и ожидал метод где-то еще в представлении. Сразу после звонка await presenterPhase1Work1.GetMethodAsync();, на ваш взгляд, вы знаете, что эта работа выполнена. Так что больше не нужно для этого булевского флага.

Это означает ваши ViewPhase1 потребности иметь другой метод для вызова метода
await presenterPhase1Work1.GetMethodAsync(); потому что и там вы не можете сделать это в конструкторе.

Ожидание завершения загрузки данных в конструкторе никогда не является хорошей идеей ИМХО, независимо от того, сделано это в фоновом потоке или нет.

+0

Спасибо! Вы правы в отношении отсутствующих ключевых слов async. также спасибо о WhenAny, не думал об этом решении. Теперь вы упомянули, что метод async вызывается в конструкторе, но в этом случае происходит сбор данных при загрузке формы, что означает, что конструктор формы (view) инициализирует личные поля презентаторов, вызывая конструкторы-докладчики - и там, где вызываются методы сбора данных. Так что в этом случае без взаимодействия с пользователем, кроме открытия формы, я нахожу dificult для использования другого подхода. Еще раз спасибо! – Libas

+0

Не можете ли вы загрузить данные в переопределении 'Form.OnLoad' или' Form.OnShown'? Таким образом, вы можете ждать там ведущего, и пользователю все равно не нужно ничего делать. –

+0

Да, и это была моя первая идея, но конструктор-презентатор по-прежнему вызовет методы асинхронной загрузки данных. Я считаю, что это также падает в плохой практике, о которой вы говорили. – Libas