Каков хороший способ сообщить о прогрессе из репозитория в пользовательский интерфейс
Я ищу хороший способ или, по крайней мере, некоторое представление о том, как чисто сообщить о прогрессе на уровне пользовательского интерфейса.
моя ситуация такова: у меня 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 демо, чтобы увидеть кодекс в действии.
Общие Понятия
в процессе создания библиотеки я столкнулся с парой "грязных" проблем. Вот как я их решил:
отчет о прогрессе из нескольких слоев
Одно время я передавал общий объект "прогресс" в каждый слой, чтобы каждый слой мог отчитываться перед ним. Такой подход-отстой. Каждый метод должен был передать объект, даже если он не использовал он.
Вместо этого, я решил использовать статический классProgress
это действует как синглтон. Таким образом, каждый слой напрямую сообщает о прогрессе (если он хочет).
Прогресс можно отслеживать путем присоединения к событию или путем опроса.
Этот подход позволяет очень легко использовать прогрессию для отчета и чтения прогресса.Потокобезопасность
Вычисление прогресса обычно необходимо только при наличии пользовательского интерфейса, который показывает прогресс фоновое задание, верно? Так что, очевидно, мне действительно нужно было подумать о безопасности нитей.
Однако я действительно хочу избежать использования типичных механизмов блокировки, потому что они дороги, и я определенно не хотел замедлять и без того медленные фоновые задачи. Итак, моим решением было использовать фоновую задачу для создания автономногоProgressChangedInfo
"объект", который можно безопасно передавать между потоками.Крест Резьбонарезной
Первоначально мой фоновый процесс сообщил о тысячах событий выполнения и вызвал обновление пользовательского интерфейса для каждого из них. Пользовательский интерфейс будет останавливаться, пытаясь догнать все обновления.
Поэтому я ввел "опрос", но обнаружил, что "важный" прогресс, такой как "копирование файла 2 из 3", был удален в пользу менее важных обновлений, таких как "файл 5% скопирован".
Теперь "важные" обновления всегда будут предпочтительнее менее важных обновлений, поэтому пользовательский интерфейс может попытаться обновить как можно быстрее самые информативные сообщения всегда отображаются, и пользовательский интерфейс всегда будет оставаться полностью отзывчивым.
прогресс конкретная информация
- A задание представляет собой процедуру с несколькими шаги. Когда шаг завершен, задача сообщает о ходе выполнения.
- отдельные методы отвечают за запуск задачи, выполнение шагов и завершение задачи
- когда вложенные задач сообщает о прогрессе, он рассчитывается соответствующим образом в направлении в целом. Количество вложенных задач не ограничено.
- есть 2 основных способа мониторинга прогресса:
- вы можете прикрепить к событию
- вы можете "опросить" прогресс
вот некоторые из особенно интересных функций:
- есть несколько умных способов расчета достижения:
- равномерно взвешенное количество шагов; например, a
for
цикл с 20 итерациями стоимостью 5% каждый - взвешенное количество шагов; например, 3 шага, которые 10%, 10%, 80%
- An неизвестный количество шагов поддерживается! Например, чтение строк из базы данных. В этом сценарии вы предоставляете "оценку", и по мере чтения каждой строки прогресс будет прогрессировать, но никогда не достигнет 100%, пока не будет полный.
- равномерно взвешенное количество шагов; например, a
- вычисление прогресса полностью лениво-поэтому, если вы реализуете вычисление прогресса на низком уровне, но нет "слушателей", то прогресс будет полностью пропущен
- несколько потокобезопасных функций встроены, поэтому опрос и обработка событий могут выполняться потоком переднего плана
- доступны несколько методов расширения, которые делают код чрезвычайно простым в использовании ... например,
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 () " и слой пользовательского интерфейса, подвергающий подгонке обратных вызовов, теперь ваш код клея может собрать это вместе, как ему нравится.