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

时间:2015-02-23 13:49:05

标签: c# entity-framework sql-server-ce async-await

我有一个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<Data> GetData()
{
    //Do IO bound activity...
    Context.DataTable1.Include(...).Load();

    //for demo purposes, just return some data
    return Context.DataTable1.Local; //(ObservableCollection<Data>)
}

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

public async Task<ObservableCollection<Data>> 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<Data>)
}

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

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

在后台线程上执行工作
public async Task<ObservableCollection<Data>> GetDataAsync()
{
    //Do IO bound activity...
    Task<ObservableCollection<Competition>> 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<Data>)
    });
    var result = await task;
    return result;
}

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

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

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

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

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

1 个答案:

答案 0 :(得分:2)

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