WPF/ReactiveUI应用程序中的ReactiveCommand阻止UI线程

WPF/ReactiveUI应用程序中的ReactiveCommand阻止UI线程,wpf,system.reactive,reactiveui,Wpf,System.reactive,Reactiveui,我对ReactiveUI是个初学者,在ReactiveCommand中有一种奇怪的行为 我想从当前不支持的数据库中查询数据 异步操作。因为我们想在 未来,我希望使用异步接口将所有内容都编写为 如果数据库已经允许异步操作。据我所知 这意味着我将数据库调用封装在数据库中的最低级别 任务 我有一个按钮,它绑定到一个ReactiveCommand和 启动数据库查询。当查询持续时,我想显示一些 有点像动画 问题是无论我尝试了什么,查询都会阻塞我的UI线程 以下是我的部分代码: public Reactiv

我对ReactiveUI是个初学者,在ReactiveCommand中有一种奇怪的行为

我想从当前不支持的数据库中查询数据 异步操作。因为我们想在 未来,我希望使用异步接口将所有内容都编写为 如果数据库已经允许异步操作。据我所知 这意味着我将数据库调用封装在数据库中的最低级别 任务

我有一个按钮,它绑定到一个ReactiveCommand和 启动数据库查询。当查询持续时,我想显示一些 有点像动画

问题是无论我尝试了什么,查询都会阻塞我的UI线程

以下是我的部分代码:

public ReactiveCommand<Unit, Unit> StartExportCommand { get; }

//The constructor of my view model
public ExportDataViewModel(IDataRepository dr)
{
    this.dr = dr;

    //...

    StartExportCommand = ReactiveCommand.CreateFromTask(() => StartExport());

    //...
}


private async Task StartExport()
{               
    try
    {                
        Status = "Querying data from database...";

        //Interestingly without this call the Status message would not even be shown!
        //The delay seems to give the system the opportunity to at least update the
        //label in the UI that is bound to "Status".
        await Task.Delay(100);

        //### This is the call that blocks the UI thread for several seconds ###
        var result = await dr.GetValues();

        //do something with result...

        Status = "Successfully completed";
    }
    catch(Exception ex)
    {
        Status = "Failed!";

        //do whatever else is necessary
    }
}

//This is the GetValues method of the implementation of the IDataRepository. 
//The dictionary maps measured values to measuring points but that should not matter here.
//ValuesDto is just some container for the values.
public Task<IDictionary<int, ValuesDto>> GetValues()
{            
    //...

    return Task<IDictionary<int, ValuesDto>>.Factory.StartNew(() =>
    {
        //### here is where the blocking calls to the database
        //### specific APIs take place

        return result;
    }, TaskCreationOptions.LongRunning);

}
public ReactiveCommand StartExportCommand{get;}
//我的视图模型的构造函数
公共导出数据视图模型(IDataRepository dr)
{
this.dr=dr;
//...
StartExportCommand=ReactiveCommand.CreateFromTask(()=>StartExport());
//...
}
专用异步任务StartExport()
{               
尝试
{                
Status=“正在从数据库中查询数据…”;
//有趣的是,如果没有这个电话,状态信息甚至不会显示!
//延迟似乎给了系统至少更新数据的机会
//UI中绑定到“状态”的标签。
等待任务。延迟(100);
//###这是一个将UI线程阻塞几秒钟的调用###
var result=等待dr.GetValues();
//做一些有结果的事情。。。
Status=“已成功完成”;
}
捕获(例外情况除外)
{
Status=“失败!”;
//做任何其他必要的事情
}
}
//这是IDataRepository实现的GetValues方法。
//字典将测量值映射到测量点,但这在这里并不重要。
//valuesdo只是一些值的容器。
公共任务GetValues()
{            
//...
返回Task.Factory.StartNew(()=>
{
//###这里是阻止对数据库的调用的地方
//###特定的API会发生
返回结果;
},TaskCreationOptions.LongRunning);
}
虽然我正在包装,但我不明白为什么这段代码会阻塞UI线程 任务中长时间运行的查询

这个模式有什么问题吗?或者我应该用另一种方式来观察观察吗

编辑1

我知道异步!=线程。但是,我认为使用TaskCreationOptions.LongRunning的任务将使阻塞代码在线程池线程上运行

编辑2


根据Andy的建议,我在任务中设置了一个断点,并查看了Debug Threads窗口。它告诉我任务正在工作线程上运行。我的UI仍然被阻塞。

在任务中创建另一个任务可能是不必要的。但是您错过了代码中最有趣的部分。这个角色很有用。对数据库的调用真的是异步的吗?在该任务中设置一个断点并使其加速。使用Debug>Windows>Threads窗口查看代码在哪个线程上运行。嗯,代码中“最有趣”的部分是对自定义ADO.NET提供程序的调用,而该提供程序不是异步的。这就是为什么我认为我必须首先使用任务。那代码真的有用吗?查看threads debug窗口很有趣:它显示在我的任务中,我在工作线程上运行。我不知道反应式命令,但我希望该命令是异步的,或者以某种方式使用异步操作。然后我会使用dapper而不是ado和一个异步扩展。任何数据库调用中代价高昂的部分都是等待它返回数据,而仅仅使所有内容自上而下异步通常可以避免任何命令阻塞。我对ADO.NET知之甚少,但是否还有其他更深入的内容将UI线程用作同步上下文?如果用一个简单的线程替换DB调用会发生什么情况。Sleep()?伙计,你的直觉多么棒!;-)使用Thread.Sleep()时,一切正常(当然没有任何数据)。这意味着它必须与COM的东西做一些事情(单线程单元同步等等)。如果你能给我一个答案,我很乐意接受!