如何将 EF 查询延迟到实际需要时执行

问题描述 投票:0回答:3

我有一个函数可以根据一系列规则确定用户与某些对象交互的权限,这些规则需要以不同方式查询数据库的多个部分。其中一个查询特别昂贵(它查询由联合和 CTE 组成的大量视图)并且该查询可能为当前用户返回 50 个左右的结果。这个特定的查询当前运行到这个函数的开始,并填充一个名为

userRoles
的列表。然后是一系列 10 个 if...else if...else if... 块,这意味着一旦这些块中的一个匹配条件,它就会跳到最后并返回结果而不处理其余部分他们。
userRoles
列表在其中的 4 个块中使用,但在第一个使用它的块之前有 3 个块。因此,如果前 3 个块中的任何一个条件匹配,那么就没有理由首先查询昂贵的视图。

在伪代码中,这是它目前的样子:

var userRoles = await DAL.GetUserRolesAsync(userId); // Expensive query using EF

if (unrelatedConditions == 1) { doSomething1(); }
else if (unrelatedConditions == 2) { doSomething2(); }
else if (unrelatedConditions == 3) { doSomething3(); }
else if (userRoles.Any(x => x.role == "red")) { logicForRedRole(); }
else if (userRoles.Any(x => x.role == "blue")) { logicForBlueRole(); }
// more else if blocks...

我想要发生的是,填充列表的查询只会在前三个块匹配失败时才会执行,现在需要第 4 个块的列表进行测试。我需要在顶部定义查询,因为与为每个块运行查询相比,只获取所有结果然后将它们过滤到每个块中需要的情况实际上性能更高。我已经尝试构建一个

Task<T>
对象,传入异步查询(
var userRolesTask = new Task<List<UserRole>>(async () => await DAL.GetUserRolesAsync(userId));
),但这不起作用,因为它实际上创建了一个
Task<Task<List<UserRole>>>
对象并且必须解包才能获得实际结果,我我不确定
await userRolesTask
是否真的开始执行查询。我可以将它更改为
var userRolesTask = new Task<List<UserRole>>(() => DAL.GetUserRolesAsync(userId).Result);
,但它只是阻塞了调用线程,我正在失去异步的好处。

所以我需要的是作为任务运行的东西,但它实际上只是包装了一个异步方法并且在等待之前不会执行。我绝对可以做一些笨拙的东西,它需要一个

Func<Task<T>>
并具有一个
T Result
属性和一个可以等待的
async Task<T> GetResult()
方法,它只会在第一次调用时执行查询。我只是希望找到内置或无缝使用的东西,而不必使用像
await userRolesTask.GetResult()
.

这样的额外构造

我在搜索中看到一些结果建议使用 Rx Observables 来做这样的事情,但这不是这个项目的选项。

c# entity-framework async-await
3个回答
1
投票

如何提取方法:

if (unrelatedConditions == 1) { doSomething1(); }
else if (unrelatedConditions == 2) { doSomething2(); }
else if (unrelatedConditions == 3) { doSomething3(); }
else if ( await IsUserRole() )
else if ...

//...

private async Task<bool> IsUserRole()
{
    var userRoles = await DAL.GetUserRolesAsync(userId); // Expensive query using EF

    if(userRoles.Any(x => x.role == "red")) 
    {
        logicForRedRole(); 
        return true;
    }
    if (userRoles.Any(x => x.role == "blue")) 
    {
        logicForBlueRole(); 
        return true;
    }
    return false;
}

还有另一种可能性是使用

Lazy<T>
.


0
投票
var userRoles;

if (unrelatedConditions == 1) { doSomething1(); }
else if (unrelatedConditions == 2) { doSomething2(); }
else if (unrelatedConditions == 3) { doSomething3(); }
else {
    userRoles = await DAL.GetUserRolesAsync(userId); // Expensive query using EF
    if (userRoles.Any(x => x.role == "red")) { logicForRedRole(); }
    else if (userRoles.Any(x => x.role == "blue")) { logicForBlueRole(); }
    else if { ... }
}

0
投票

您可以将其实现并声明为 IQuerable。当您需要它时,您可以在该函数中调用 ToList() 方法。所以会在ToList()方法调用后执行。

© www.soinside.com 2019 - 2024. All rights reserved.