如何避免从构造函数调用长操作

How to avoid calling long operations from constructor

我使用 MVVM,我必须创建一个 ViewModel class,它应该在打开 View 时加载大量数据。

基本上,当我创建视图模型时,它应该使用数据库并获取数据。

我首先使用了这种方法:

public class MainViewModel
{
 public MainViewModel()
 {
   //set some properties
   Title="Main view";
   //collect the data
   StartLongOperation();
 }

 private void StartLongOperation()
 {
   Thread t=new Thread(...);
   t.start();
 }
}

它在不阻塞 UI 线程的情况下工作并加载数据。

后来找到了this guideline构造函数的使用方法,不建议从构造函数开始长时间运行

√ DO minimal work in the constructor.

Constructors should not do much work other than capture the constructor parameters. The cost of any other processing should be delayed until required.

就我而言,打开视图时需要数据。

我的第一个想法是使用 事件

我应该如何避免从构造函数中调用长操作?最佳做法是什么?

避免调用很简单,拆分成2个方法即可;打开视图时或设置数据上下文后调用的构造函数和 GetData 方法。

原因就是管理期望。如果您没有编写代码并正在为其他人的视图模型编写新视图,您会期望构造函数开始访问数据库吗?还是您希望它只是构建一个视图模型,您需要进行第二次调用以启动获取数据?

也许可以使用工厂模式来避免有 MainViewModel 但未填充。

    public class VMFactory
    {
        public async Task<MainViewModel> GetVM()
        {
            MainViewModel vm = new MainViewModel();
            await vm.LongOperation();
            return vm;
        }
    }

    public class MainViewModel
    {
        public MainViewModel()
        {
            //set some properties
            Title = "Main view";
        }

        public async Task LongOperation()
        {
            (...)
        }
    }

或许更好。将长 运行 方法从 MainViewModel 移到存储库或服务

    public class VMRepository
    {
        public async Task LongOperation()
        {
            (...)
        }

        public async Task<MainViewModel> GetVM()
        {
            MainViewModel vm = new MainViewModel();
            vm.DataWhichTakesAlongTime  = await LongOperation();
            return vm;
        }
    }

    public class MainViewModel
    {
        public MainViewModel()
        {
            //set some properties
            Title = "Main view";
        }

        public object DataWhichTakesAlongTime { get; set; }
    }

老实说,尽管围绕这个问题的对话听起来您只是将构造函数用作 'LoadDataNow' 命令的方便触发器,实际上您应该添加一个 ICommand,将其绑定到查看(已加载)添加加载微调器和已完成的事件等

有争议的是,我可能还建议您添加一个控制器 Class 来实例化存储库视图和 vm,并在视图上调用 'LoadData' 方法。我知道的不是很 MVVM,但基本上可以做你的 IoC 容器做的同样的事情,而不必跳过配置

Miguel Castro 在他的一门出色的 Pluralsight 课程中谈到了解决这个问题的方法。他绑定到名为 ViewLoaded 的视图模型中的 属性 ,当视图加载时显然会绑定,这反过来会调用你的 long 运行 方法。

所以这进入了视图(或者所有视图的基础 class 以帮助重用):

    public ViewBase()
    {
        // Programmatically bind the view-model's ViewLoaded property to the view's ViewLoaded property.
        BindingOperations.SetBinding(this, ViewLoadedProperty, new Binding("ViewLoaded"));
    }

    public static readonly DependencyProperty ViewLoadedProperty =
        DependencyProperty.Register("ViewLoaded", typeof(object), typeof(UserControlViewBase),
        new PropertyMetadata(null));

这是 ViewModel 基础 class 代码:

public object ViewLoaded
{
    get
    {
        OnViewLoaded();
        return null;
    }
}

protected virtual void OnViewLoaded() { }

只需覆盖 ViewModel 中的 OnViewLoaded() 方法并从那里调用长 运行 方法。

我不知道,也许这是错误的,但有时我会做(如果我需要重新运行参数)

public class MainViewModel
    {
        public MainViewModel()
        {
            //set some properties
            Title = "Main view";
        }
        public static string GetMainViewModelString()
        {
            var mainViewModel = new MainViewModel();
            return mainViewModel.GetString();
        }
        public string GetString()
        {
            /*your code*/
        }
    }

然后调用

var stringData = MainViewModel.GetMainViewModelString();

但当需要时,我会从构造函数中调用一些操作

使用您的视图生命周期来执行此方法。您可以使用 Tasks 来简化执行,并且可以绑定到其他属性以显示进度。使用 Windows 应用商店视图显示的示例。

视图模型:

public class MainViewModel
{
    public MainViewModel()
    {
        this.Title = "Main view";
    }

    public async Task StartLongOperationAsync()
    {
        this.IsLoading = true;

        await Task.Run(() =>
        {
            //do work here
        });

        this.IsLoading = false;
    }
}

在视图上:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    await ((MainViewModel)this.DataContext).StartLongOperationAsync();
}