从Repository向UI报告进度的好方法是什么?
本文关键字:方法 是什么 Repository UI 报告 | 更新日期: 2023-09-27 18:19:05
我正在寻找一种好方法,或者至少是一些见解,关于如何干净地向UI层报告进度。
我的情况是这样的:我在Infrastructure Layer
中得到一个Repository
,它与Data Layer
通信。当这两个正在工作的UI
目前没有线索正在发生什么,只是被动地等待结果(使用一个可观察的)
我的第一个想法是公开UI绑定到的Repository中的事件。但我觉得这可能会很乱。我很想听听你们是怎么解决的。
我做过很多后台处理和报告进度的工作,所以我完全理解这是多么的"混乱"。它可以得到。
我创建了一个开源库,它确实为报告进度提供了一个很好的框架,特别是在"嵌套"中。场景!查看进度。
我建议看一下项目中的一些单元测试,因为那里有很多使用示例。还有一个可运行的演示程序,可以查看代码的运行情况。
的一般概念在创建库的过程中,我遇到了几个"混乱"的"问题。我是这样解决的:
从多层报告进度
有一次,我正在通过一个普通的"进度"考试。对象放到每一层,这样每一层都可以向它报告。这种方法很糟糕。每个方法都必须传递对象,即使它不使用它。
相反,我决定使用一个静态类Progress
,它的行为就像一个单例。这样,每一层都可以直接报告进度(如果需要的话)。
可以通过附加到事件或轮询来监视进度。
这种方法使得使用Progression来报告和读取进度非常容易。线程安全
计算进度通常只在UI显示后台任务进度时才需要,对吧?显然,我必须认真考虑线程安全性。
然而,我确实希望避免使用典型的锁定机制,因为它们代价高昂,而且我绝对不想减慢已经很慢的后台任务的速度。所以,我的解决方案是使用后台任务来创建一个独立的ProgressChangedInfo
对象,它可以安全地在线程之间传递。交叉线程
最初,我的后台进程报告数千个进程事件,并为每个事件调用UI更新。UI会停滞不前,试图赶上所有的更新。
所以我引入了"民意调查",但我发现这"很重要"诸如"复制文件2/3"之类的进度被放弃,取而代之的是一些不太重要的更新,如"复制文件5%"。
现在,"important"更新总是比不太重要的更新更受青睐,因此UI可以尝试尽可能快地更新,总是显示最具信息量的消息,并且UI将始终保持完全响应。
进展具体信息
- 一个Task表示一个过程有几个步骤。当一个步骤完成后,Task会报告进度。
- 单个方法负责启动任务、完成步骤和结束任务
- 当嵌套的任务报告进度时,它会根据总体进度进行计算。嵌套任务的数量没有限制。
- 有两种主要的监测进展的方法:
- 您可以附加到一个事件
- 你可以"投票"
以下是一些特别酷的功能:
- 有几个聪明的方法来计算进度:
- 平均加权步数;例如,
for
循环有20次迭代,每次值5% - 加权步数;例如,3个步骤占10%、10%、80%
- 支持未知的步数!例如,从数据库中读取行。在这个场景中,您提供了一个"估计",当读取每一行时,进度将向前推进,但在完成之前永远不会达到100%。
- 进度计算是完全LAZY的-所以如果你在低级别实现进度计算,但没有"监听器",那么进度将被完全跳过
- 几个线程安全的特性是内置的,所以轮询和事件处理可以由前台线程完成
- 几个扩展方法是可用的,使代码非常容易使用…例如,
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();
}
使用事件或(裸)委托。
由于您可能有多个调用,委托参数可能是最好的(不必从事件取消注册)。
// Repository
public delegate void ReportProgress(int percentage);
public IEnumerable<X> GetSomeRecords(..., ReportProgress reporter)
{
...;
if (reporter != null)
reporter(i * 100 / totalRecords);
}
如何返回从0-100报告进度的IObservable<int>
?这不仅具有返回进度的优点,而且还允许您在出现错误时返回错误(通过OnError)。
经典的方法是让你的非用户可见层暴露"[Unr|R]egisterProgressCallback()"方法和UI层暴露合适的回调,现在你的胶水代码可以把它们放在一起,因为它喜欢