【问题标题】:MVVM c# how to load async data into a property?MVVM c#如何将异步数据加载到属性中?
【发布时间】:2021-05-21 09:32:19
【问题描述】:

我想知道是否有更好的方法将异步数据加载到属性中。现在我创建一个异步函数并在属性的 Get 部分引发一个任务,如下所示:

private ObservableCollection<CProyecto> prope;

public ObservableCollection<CProyecto> Prope
{
    get 
    {
        if (prope == null)
        {
            Task.Run(()=> LoadData()).Wait();
        }

        return proyectos;
    }
    set 
    { 
        prope = value; 
        RaisePropertyChanged(); 
    }
}

async private Task LoadData() 
{
    Prope = await clsStaticClassDataLoader.GetDataFromWebService();
}

这种方法有效,但我不喜欢使用 .Wait,因为如果服务没有快速响应,它会冻结屏幕。

你能在这件事上指导我吗?

提前致谢

【问题讨论】:

  • 如果你调用Wait()方法来阻塞调用线程,那么启动一个新任务有什么意义......?
  • 关于异步属性,您应该阅读以下内容:blog.stephencleary.com/2013/01/async-oop-3-properties.html
  • 使用loaded event 从外部来源获取数据。使事件异步并绑定到您的集合。
  • 最差。财产。永远。
  • 埃尔多,loaded_event?从 xaml 页面? .谢谢

标签: c# wpf mvvm


【解决方案1】:

我建议您阅读我在 async MVVM data-binding 上的 MSDN 文章。我有a library (github link),它提供了一个NotifyTask&lt;T&gt; type,可以这样使用:

public class MyClass : INotifyPropertyChanged
{
  public NotifyTask<ObservableCollection<CProyecto>> Prope { get; private set; }

  public MyClass()
  {
    // Synchronously *start* the operation.
    Prope = NotifyTask.Create(LoadDataAsync());
  }

  async private Task<ObservableCollection<CProyecto>> LoadDataAsync()
  {
    return await clsStaticClassDataLoader.GetDataFromWebService();
  }
}

然后您的数据绑定将在Prope.Result 上运行。

这种方法的优点是您还可以使用数据绑定来隐藏/显示繁忙指示器 (Prope.IsNotCompleted)、在数据可用时显示控件 (Prope.IsSuccessfullyCompleted) 和错误通知 (Prope.IsFaulted / @987654334 @)。

此外,如果您愿意,您可以指定一个非null 默认值:

Prope = NotifyTask.Create(LoadDataAsync(), new ObservableCollection<CProyecto>());

【讨论】:

  • 库现在在github上找到了:github.com/StephenCleary/Mvvm.Async
  • 好的,愚蠢的问题,我如何将这个库加载到我的项目中?我正在使用旧版本的 INotifyTaskCompletion 和 NotifyTaskCompletion.Create()。我在 Stephen Cleary 的一篇旧帖子中找到了它,它位于 Nito.AsyncEx.Strong 的 Nuget 包中。
  • @FLAdmin:很抱歉造成混乱;我认为异步 MVVM 类型比我编写的任何其他代码都经历了更多的包重命名。他们目前在:nuget.org/packages/Nito.Mvvm.Async
  • 非常感谢斯蒂芬。我会导入那个包。实际上,我从您的文章中复制了您的 NotifyTaskCompletion 类,效果很好。让我继续前进。 docs.microsoft.com/en-us/archive/msdn-magazine/2014/march/…
【解决方案2】:

我处理这个的方式是在构造对象时开始加载属性的过程,但我没有等待结果。由于该属性会在填充时发出通知,因此绑定工作得很好。本质上它是这样工作的:

public class MyClass : INotifyPropertyChanged
{
    private ObservableCollection<CProyecto> prope;

    public ObservableCollection<CProyecto> Prope
    {
        get { return prope; }
        set { prope = value; RaisePropertyChanged(nameof(Prope)); }
    }

    public MyClass()
    {
        // Don't wait or await.  When it's ready
        // the UI will get notified.
        LoadData();
    }

    async private Task LoadData() 
    {
        Prope = await clsStaticClassDataLoader.GetDataFromWebService();
    }
}

这很好用,不会导致 UI 出现任何延迟或卡顿。如果您希望集合永远不会是 null(IMO 的良好做法),您可以使用空集合预初始化 prope 字段。

【讨论】:

  • 这是很好的第一步,但会默默地吞下LoadData 抛出的错误。更全面的方法是在 LoadData 中使用顶级 try/catch 并使用错误通知更新您的 UI。
  • 同意。我正在描述我采用的方法。但是,您有正确的位置来进行错误处理。
  • 但是你不能从构造函数中调用异步方法,这个属性也是同样的问题。你收到了这个警告msdn.microsoft.com/en-us/library/hh873131.aspx
  • @KillemAll,您会收到警告,因为您无法 await 回复。这里的目标不是等待,而是开始加载数据的过程。我们以另一种方式收到工作完成的通知。
  • 我就像你说的那样做,唯一没有得到警告的方法是做 LoadData().wait(); 或者我错过了什么?
【解决方案3】:

您当前对 Prope 属性的实现没有多大意义。在后台线程上执行LoadData 方法是没有意义的,因为当您调用Wait() 时无论如何都会阻塞主线程。你不妨直接在LoadData()方法返回的任务上调用Wait()

//BAD IMPLEMENTATION!
private ObservableCollection<CProyecto> prope;
public ObservableCollection<CProyecto> Prope
{
    get
    {
        if (prope == null)
            LoadData().Wait();
        return proyectos;
    }
    set { prope = value; RaisePropertyChanged(); }
}

上述实现仍然是一个糟糕的实现。属性的 getter 不应该执行异步操作。你应该阅读@Stephen Cleary 关于这个主题的博文:https://blog.stephencleary.com/2013/01/async-oop-3-properties.html

...在他的AsyncEx 库中查看他的NotifyTaskCompletion 类型:https://github.com/StephenCleary/AsyncEx

【讨论】:

  • 好吧,看起来没有意义,但实际上它有意义,如果我不这样做,当我调用我的 web 服务时系统会挂起。我想那是因为我调用服务的方式 var Response = client.GetAsync(url).Result;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-01
  • 1970-01-01
  • 2021-03-14
  • 2023-03-27
  • 1970-01-01
  • 2021-06-26
  • 1970-01-01
相关资源
最近更新 更多