Каков хороший способ сообщить о прогрессе из репозитория в пользовательский интерфейс

Я ищу хороший способ или, по крайней мере, некоторое представление о том, как чисто сообщить о прогрессе на уровне пользовательского интерфейса.

моя ситуация такова: у меня Repository на Infrastructure Layer, который общается с Data Layer. Пока эти двое работают UI В настоящее время не имеет понятия о том, что происходит, и только пассивно ждет результатов (используя наблюдаемый)

мои первые мысли были выставить события в репозитории, к которому привязывается пользовательский интерфейс. Но я чувствую, что это может получиться довольно грязно. Я хотел бы услышать как вы решили эту.

4 ответов


Uese либо события, либо (голые) делегаты.

поскольку у вас, вероятно, есть несколько вызовов, параметр делегата может быть лучшим (не нужно отменять регистрацию событий).

// Repository
public delegate void ReportProgress(int percentage);

  public IEnumerable<X> GetSomeRecords(..., ReportProgress reporter)
  {
      ...;
      if (reporter != null)
          reporter(i * 100 / totalRecords);  
  }

я проделал большую работу с фоновой обработкой и отчетностью о прогрессе, поэтому я полностью понимаю, как "грязный" он может получить.

Я создал библиотеку с открытым исходным кодом, которая действительно обеспечивает хорошую основу для отчетности о прогрессе, особенно в "вложенных" сценариях! Проверьте прогресс.

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

Общие Понятия

в процессе создания библиотеки я столкнулся с парой "грязных" проблем. Вот как я их решил:

  1. отчет о прогрессе из нескольких слоев
    Одно время я передавал общий объект "прогресс" в каждый слой, чтобы каждый слой мог отчитываться перед ним. Такой подход-отстой. Каждый метод должен был передать объект, даже если он не использовал он.
    Вместо этого, я решил использовать статический класс Progress это действует как синглтон. Таким образом, каждый слой напрямую сообщает о прогрессе (если он хочет).
    Прогресс можно отслеживать путем присоединения к событию или путем опроса.
    Этот подход позволяет очень легко использовать прогрессию для отчета и чтения прогресса.

  2. Потокобезопасность
    Вычисление прогресса обычно необходимо только при наличии пользовательского интерфейса, который показывает прогресс фоновое задание, верно? Так что, очевидно, мне действительно нужно было подумать о безопасности нитей.
    Однако я действительно хочу избежать использования типичных механизмов блокировки, потому что они дороги, и я определенно не хотел замедлять и без того медленные фоновые задачи. Итак, моим решением было использовать фоновую задачу для создания автономного ProgressChangedInfo "объект", который можно безопасно передавать между потоками.

  3. Крест Резьбонарезной
    Первоначально мой фоновый процесс сообщил о тысячах событий выполнения и вызвал обновление пользовательского интерфейса для каждого из них. Пользовательский интерфейс будет останавливаться, пытаясь догнать все обновления.
    Поэтому я ввел "опрос", но обнаружил, что "важный" прогресс, такой как "копирование файла 2 из 3", был удален в пользу менее важных обновлений, таких как "файл 5% скопирован".
    Теперь "важные" обновления всегда будут предпочтительнее менее важных обновлений, поэтому пользовательский интерфейс может попытаться обновить как можно быстрее самые информативные сообщения всегда отображаются, и пользовательский интерфейс всегда будет оставаться полностью отзывчивым.

прогресс конкретная информация

  • A задание представляет собой процедуру с несколькими шаги. Когда шаг завершен, задача сообщает о ходе выполнения.
  • отдельные методы отвечают за запуск задачи, выполнение шагов и завершение задачи
  • когда вложенные задач сообщает о прогрессе, он рассчитывается соответствующим образом в направлении в целом. Количество вложенных задач не ограничено.
  • есть 2 основных способа мониторинга прогресса:
    • вы можете прикрепить к событию
    • вы можете "опросить" прогресс

вот некоторые из особенно интересных функций:

  • есть несколько умных способов расчета достижения:
    • равномерно взвешенное количество шагов; например, a for цикл с 20 итерациями стоимостью 5% каждый
    • взвешенное количество шагов; например, 3 шага, которые 10%, 10%, 80%
    • An неизвестный количество шагов поддерживается! Например, чтение строк из базы данных. В этом сценарии вы предоставляете "оценку", и по мере чтения каждой строки прогресс будет прогрессировать, но никогда не достигнет 100%, пока не будет полный.
  • вычисление прогресса полностью лениво-поэтому, если вы реализуете вычисление прогресса на низком уровне, но нет "слушателей", то прогресс будет полностью пропущен
  • несколько потокобезопасных функций встроены, поэтому опрос и обработка событий могут выполняться потоком переднего плана
  • доступны несколько методов расширения, которые делают код чрезвычайно простым в использовании ... например, IEnumerable<T>.WithProgress() сообщит обо всем прогрессе при переборе!

вот некоторый код в действии:

public void PerformSomeTask() {
    // This task has 2 steps; the first step takes about 20%, and the second takes 80%:
    Progress.BeginWeightedTask(20, 80);

    // Start the first step (20%):
    Progress.NextStep();
    TaskA();

    // Start the second step (80%):
    Progress.NextStep();
    TaskB();

    // Finished!
    Progress.EndTask();     
}


public void TaskA() {
    int count = 20;
    // This task has 20 steps:
    Progress.BeginFixedTask(count);

    for (int i = 0; i < count; i++) {
        Progress.NextStep();
        // ...
    }

    Progress.EndTask();
}
public void TaskB() {
    // This task has an unknown number of steps
    // We will estimate about 20, with a confidence of .5
    Progress.BeginUnknownTask(20, .5);

    while (database.ReadRow()) {
        Progress.NextStep();
        // ...
    }
    Progress.EndTask();
}

Как насчет возврата IObservable<int> какие отчеты о прогрессе от 0-100? Это имеет не только преимущество возврата прогресса, но и позволяет вернуть ошибку (через OnError), если что-то пойдет не так.


классическим способом для этого было бы, чтобы ваши слои, не видимые пользователем, выставляли методы" [Unr|R]egisterProgressCallback () " и слой пользовательского интерфейса, подвергающий подгонке обратных вызовов, теперь ваш код клея может собрать это вместе, как ему нравится.