MVVM和构造函数注入中的后台加载

我有一个问题,关于在WPF .NET 4.0中使用ViewModel加载大量数据的方式和位置(所以没有async / await:/)。

这是我的ViewModel:

public class PersonsViewModel : ViewModelBase { private readonly IRepository _personRepository; private IEnumerable _persons; public IEnumerable Persons { get { return _persons; } private set { _persons = value; OnPropertyChanged("Persons"); } } public PersonsViewModel(IRepository personRepository) { if (personRepository == null) throw new ArgumentNullException("personRepository"); _personRepository = personRepository; } } 

这个ViewModel用在Window中,我需要在Window打开时加载所有人。 我想到了很多解决方案,但我无法确定哪个是最好的(或者可能有更好的方法)。 我有两个约束: – 所有数据必须加载到另一个线程,因为它可能需要几秒钟加载(数据库中的大量数据),我不想冻结UI。 – ViewModel必须是可测试的。

– = [第一个解决方案:延迟加载] = –

 public class PersonsViewModel : ViewModelBase { private IEnumerable _persons; public IEnumerable Persons { get { if (_persons == null) _persons = _personRepository.GetAll(); return _persons; } } } 

我不喜欢这个解决方案,因为数据是在主线程中加载的。

– = [第二个解决方案:加载事件] = –

 public class PersonsViewModel : ViewModelBase { // ... private Boolean _isDataLoaded; public Boolean IsDataLoaded { get { return _isDataLoaded; } private set { _isDataLoaded = value; OnPropertyChanged("IsDataLoaded"); } } public void LoadDataAsync() { if(this.IsDataLoaded) return; var bwLoadData = new BackgroundWorker(); bwLoadData.DoWork += (sender, e) => e.Result = _personRepository.GetAll(); bwLoadData.RunWorkerCompleted += (sender, e) => { this.Persons = (IEnumerable)e.Result; this.IsDataLoaded = true; }; bwLoadData.RunWorkerAsync(); } } public class PersonWindow : Window { private readonly PersonsViewModel _personsViewModel; public PersonWindow(IRepository personRepository) { _personsViewModel = new PersonsViewModel(personRepository); this.Loaded += PersonWindow_Loaded; } private void PersonWindow_Loaded(Object sender, RoutedEventArgs e) { this.Loaded -= PersonWindow_Loaded; _personsViewModel.LoadDataAsync(); } } 

我真的不喜欢这个解决方案,因为它强制ViewModel的用户调用LoadDataAsync方法。

– = [第三种解决方案:在ViewModel构造函数中加载数据] = –

 public class PersonsViewModel : ViewModelBase { // ... public PersonsViewModel(IRepository personRepository) { if (personRepository == null) throw new ArgumentNullException("personRepository"); _personRepository = personRepository; this.LoadDataAsync(); } private Boolean _isDataLoaded; public Boolean IsDataLoaded { get { return _isDataLoaded; } private set { _isDataLoaded = value; OnPropertyChanged("IsDataLoaded"); } } public void LoadDataAsync() { if(this.IsDataLoaded) return; var bwLoadData = new BackgroundWorker(); bwLoadData.DoWork += (sender, e) => e.Result = _personRepository.GetAll(); bwLoadData.RunWorkerCompleted += (sender, e) => { this.Persons = (IEnumerable)e.Result; this.IsDataLoaded = true; }; bwLoadData.RunWorkerAsync(); } } 

在此解决方案中,ViewModel的用户不需要调用额外的方法来加载数据,但它违反了单一责任原则,因为Mark Seeman在他的书“dependency injection”中说:“保持构造函数不受任何逻辑的影响。 SRP意味着成员应该只做一件事,现在我们使用构造函数来注入依赖关系,我们宁愿保持它没有其他问题“。

有什么想法以正确的方式解决这个问题?

如果不知道如何将ViewModel与视图绑定,很难给出准确的答案。

一种做法是拥有一个“导航INavigationAware ”ViewModel(一个ViewModel,它实现一个像INavigationAware这样的INavigationAware ,让你的导航服务在实例化ViewModel / View时将它们绑定在一起。这就是Prism的FrameNavigationService的工作方式。

 public interface INavigationAware { Task NavigatedTo(object param); Task NavigatedFrom(...) } public class PersonWindow : ViewModelBase, INavigationAware { } 

如果 ViewModel实现了INavigationAwareNavigatedTo实现初始化代码,该代码将从导航服务中INavigationAware

适用于Windowsapp store的Prism参考:

  • INavigationAware
  • FrameNavigationService =>查看NavigateToCurrentViewModel方法