使用Entity Framework异步方法和SQL Server Compact阻止行为

我有一个MVVM应用程序调用数据服务来获取一些数据绑定。 数据服务通过Entity Framework 6访问SQL Server Compact(v4.0)数据库。

数据(当前)需要几秒钟才能加载,当同步调用时(这并不奇怪)会阻止GUI线程。 我假设大部分都是IO绑定所以我添加了一个等效的Async方法以异步方式执行数据加载,以允许GUI保持响应。 但是,当我使用EF Async方法时,它似乎没有任何区别,GUI线程仍会阻塞大致相同的时间。

我知道使用Async调用不会将工作移动到不同的线程,但是我已经假定这个活动的大部分是IO绑定的。 因此,使用EF Async方法应该允许调用(GUI)线程继续做其他工作,但它没有任何区别。

例如,如果我编写LoadData方法以使用EF同步加载方法,则GUI将阻塞,直到加载数据:

public ObservableCollection GetData() { //Do IO bound activity... Context.DataTable1.Include(...).Load(); //for demo purposes, just return some data return Context.DataTable1.Local; //(ObservableCollection) } 

加载数据的Async方法如下所示:

 public async Task<ObservableCollection> GetDataAsync() { //Do IO bound activity... var task = Context.DataTable1.Include(...).LoadAsync(); await task; //for demo purposes, just return some data return Context.DataTable1.Local; //(ObservableCollection) } 

令人惊讶的是(对我来说)我得到了相同的结果,并且它阻止了调用线程大致相同的时间长度(我把一个秒表放在上面)。

我开始认为除了数据库IO绑定工作之外,可能会有一些最小量的CPU绑定活动导致阻塞。 所以我终于尝试使用Task.Run()在后台线程上执行工作:

 public async Task<ObservableCollection> GetDataAsync() { //Do IO bound activity... Task<ObservableCollection> task = Task.Run(async () => { //Do IO bound activity... var task = Context.DataTable1.Include(...).LoadAsync(); await task; //for demo purposes, just return some data return Context.DataTable1.Local; //(ObservableCollection) }); var result = await task; return result; } 

有了这个,GUI显然不会阻塞并在整个时间内响应。

我已经阅读了很多这方面的文章,包括这里和这里的post以及Stephen Cleary的博客文章,关于何时不( 这里 )以及何时( 这里 )使用Task.Run。 我知道上面的上一个例子仍然是阻塞的,它只是阻塞了一个线程池线程。 我真正不明白的是,为什么在访问EF异步方法时它似乎没有提供任何好处?

可能是因为有IO活动正在进行,还有足够的CPU绑定工作来导致阻塞? 响应时间肯定要超过50毫秒,接近2或3秒才能运行所有查询,所以这种类型的活动可以开始certificate使用Task.Run是正确的吗?

或者,我是否错误地编写了异步方法,为什么它仍然阻塞?

我还想知道,对于EF的SQL Compact提供程序的Async方法是否出于某些原因而退回到标准的同步调用?

SQL Server Compact ADO.NET提供程序不提供任何Async API。 但是加载数据永远不会花费几秒,除非您获取1000行。 启动应用程序时初始化dbContext.connection对象,并在应用程序的生命周期内保留空和未使用状态。 第一次还有dbContext初始化的代价。