我正在尝试从数据库中分批读取大量行,并向用户报告进度;即,如果我正在加载100个“块”,则在每个块加载时报告进度。
我正在使用C# 4.0中的TPL从数据库中读取这些块,然后将完整结果集交给另一个可以使用它的任务。我觉得TPL比BackgroundWorker等更好地控制任务取消和交接,但似乎没有内置的方法来报告任务的进度。
这是我实现的向WPF进度条报告进度的解决方案,我想确保这是适当的,并且是否应该采取更好的方法。
我首先创建了一个简单的接口来表示改变进度:
我正在使用C# 4.0中的TPL从数据库中读取这些块,然后将完整结果集交给另一个可以使用它的任务。我觉得TPL比BackgroundWorker等更好地控制任务取消和交接,但似乎没有内置的方法来报告任务的进度。
这是我实现的向WPF进度条报告进度的解决方案,我想确保这是适当的,并且是否应该采取更好的方法。
我首先创建了一个简单的接口来表示改变进度:
public interface INotifyProgressChanged
{
int Maximum { get; set; }
int Progress { get; set; }
bool IsIndeterminate { get; set; }
}
这些属性可以绑定到WPF视图中的进度条,接口由支持ViewModel实现,负责启动数据加载并最终报告总体进度(此示例简化):
public class ContactsViewModel : INotifyProgressChanged
{
private IContactRepository repository;
...
private void LoadContacts()
{
Task.Factory.StartNew(() => this.contactRepository.LoadWithProgress(this))
.ContinueWith(o => this.UseResult(o));
}
}
您会注意到我将ViewModel作为INotifyProgressChanged传递给存储库方法,这就是我想确保自己没有做错事情的地方。
我的思路是,为了报告进度,实际执行工作的方法(即存储库方法)需要访问INotifyProgressChanged接口,以便最终更新视图来报告进度。以下是存储库方法的简短示例:
public class ContactRepository : IContactRepository
{
...
public IEnumberable<Contact> LoadWithProgress(INotifyProgressChanged indicator)
{
var resultSet = new List<Contact>();
var query = ... // This is a LINQ to Entities query
// Set the maximum to the number of "pages" that will be iterated
indicator.Maximum = this.GetNumberOfPages(query.Count(), this.pageSize);
for (int i = 0; i < indicator.Maximum; i++)
{
resultSet.AddRange(query.Skip(i * this.pageSize).Take(this.pageSize));
indicator.Progress += 1; // As each "chunk" is loaded, progress is updated
}
// The complete list is returned after all "chunks" are loaded
return resultSet.AsReadOnly();
}
}
这就是仓库通过ViewModel最终向View报告进度的方式。这种方法正确吗?我是否正确使用了TPL,是否违反了重要规则等?这个解决方案正在工作,进度如预期地报告,我只想确保自己不会失败。