同时运行多个EntityFramework数据库查询

16
我正在尝试并行运行三个数据库查询,但我不确定我是否做得正确。
我已经创建了三个函数,每个函数都对数据库进行查询。
private static async Task<string> getAccountCodeAsync(string deviceId)
{
    long deviceIdLong = long.Parse(deviceId);
    using (var db = new NetworksEntities())
    {
        return db.Devices.Where(x => x.DeviceId == deviceIdLong)
            .Select(x => x.AccountCode)
            .FirstOrDefault();
    }
}

private static async Task<string> getDeviceTypeAsync(string deviceId)
{
    long deviceIdLong = long.Parse(deviceId);
    using (var db = new NetworksEntities())
    {
        return db.Devices.Where(x => x.DeviceId == deviceIdLong)
            .Select(x => x.DeviceType)
            .FirstOrDefault()
            .DeviceType;
    }
}

private static async Task<string> getUserNameAsync(string userId)
{
    int userIdInt;
    Int32.TryParse(userId, out userIdInt);
    using (var db = new NetworksEntities())
    {
        return db.Users.Where(x => x.UserId == userIdInt)
            .Select(x => x.Email)
            .FirstOrDefault();
    }
}   

我在我的代码中运行了三个任务:
var TaskAccountCode = await getAccountCodeAsync(deviceId);
var TaskDeviceType = await getDeviceTypeAsync(deviceId);
var TaskUsername = await getUserNameAsync(userId);
await Task.WhenAll();   
// use the results from my three tasks to make a new insert into the db.

我正在做的事情实际上是同时运行我的三个数据库查询吗?

编辑:

private static async Task<string> getAccountCodeAsync(string deviceId)
{
    long deviceIdLong = long.Parse(deviceId);
    using (var db = new NetworksEntities())
    {               
        return db.Devices.Where(x => x.DeviceId == deviceIdLong)
            .Select(x => x.AccountCode)
            .FirstOrDefaultAsync();
    }
}

1
不,如果您这样调用它们,它们将同步运行。使用await,它将首先运行第一个方法,然后是第二个方法...等等。要并行运行它们,请按如下方式执行:Task.WhenAll(TaskAccountCode,TaskDeviceType,TaskUsername),并删除await。 - Mihai Hantea
1
请参考以下链接:https://dev59.com/JmIk5IYBdhLWcg3wn_f1 - Mukus
2个回答

17

你需要更改代码的最后一部分,以使其可以并行运行:

var taskAccountCode = getAccountCodeAsync(deviceId);
var taskDeviceType = getDeviceTypeAsync(deviceId);
var taskUsername = getUserNameAsync(userId);
await Task.WhenAll(taskAccountCode, taskDeviceType, taskUsername);
var accountCode = taskAccountCode.Result;
var deviceType = taskDeviceType.Result;
var username  = taskUsername.Result;

注意只有一个await。在您原始的代码中,您一个接一个地等待每个任务,使它们按顺序而非并行运行。

此外,getAccountCodeAsync等方法并不是真正的异步方法(您应该会收到编译器警告)。然而,Entity Framework 6支持异步操作,并且为了使用它,您应该将FirstOrDefault替换为FirstOrDefaultAsync。对于每个并行操作,您都必须使用单独的上下文,这正是您正在做的。


我需要在 Task.WhenAll(); 中指定任务名称吗? - Zapnologica
你说得对,我确实在异步方法上收到了编译器警告,但是没有异步读取,我知道有一个异步保存更改。这会将每个任务都运行在自己的线程上吗? - Zapnologica
1
是的,你必须在 Task.WhenAll(); 中指定任务名称。 - Martin Liversage
我尝试改用firstOrDefaultAsync(),但我的编译器不喜欢它。 - Zapnologica
目前正在使用EF 6.1.0版本,但是找不到FirstOrDefaultAsync()方法,唯一能够找到的是FindAsync()方法。我这里做错了什么吗? - Zapnologica
显示剩余7条评论

2

如果您使用await foreach方法调用它们,那么它们将一个接一个地运行,首先运行第一个方法,然后是第二个...等等。并且在最后一部分的await Task.WhenAll()中,您没有提供需要等待完成的任务。

要并行运行它们,您必须像这样做:

var TaskAccountCode = getAccountCodeAsync(deviceId);
var TaskDeviceType = getDeviceTypeAsync(deviceId);
var TaskUsername = getUserNameAsync(userId);
await Task.WhenAll(TaskAccountCode, TaskDeviceType,TaskUsername);   

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接