【发布时间】:2018-12-27 13:28:47
【问题描述】:
我有一个继承的实体框架查询,其中包括几个总和,缩减示例:-
from c in db.Clients
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = c.ClientTransactions.Select(ct => ct.Amount)
.DefaultIfEmpty(0m).Sum(),
}).ToList();
随着客户端数量和事务数量的增长,这个查询显然变得越来越慢。
理想情况下,我希望存储余额而不是每次都计算它们,但目前系统不这样做,而且实施起来将是一个非常大的变化,所以现在我只是尝试一个带 -帮助修复。
我正在尝试实施的解决方法是不为对它们不感兴趣的人进行 Sum 计算(有几个,上面的示例只有一个)。
我的第一次尝试是简单地使用三元条件运算符来确定是否进行计算:-
from c in db.Clients
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = ClientSearchExcludeCurrentBalance ? 0m :
c.ClientTransactions.Select(ct => ct.fAmount).DefaultIfEmpty(0m).Sum(),
}).ToList();
事实证明,这样做的问题在于,无论条件 (ClientSearchExcludeCurrentBalance) 的值如何,双方仍然计算,然后三元决定使用哪一个。因此,即使将条件设置为 false,Sum 仍然会得到处理,并且查询时间过长。
注释掉总和,如下...
from c in db.Clients
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = ClientSearchExcludeCurrentBalance ? 0m : 0m,
//c.ClientTransactions.Select(ct => ct.fAmount).DefaultIfEmpty(0m).Sum(),
}).ToList();
... 现在又好又快了,所以即使不使用三元也肯定会运行它。
所以,有了这个想法,我尝试使用一个表达式来代替:-
Expression<Func<Client, Decimal>> currentBalance = c => 0m;
if (!ClientSearchExcludeCurrentBalance)
{
currentBalance = c => c.ClientTransactions
.Select(ct => ct.Amount).DefaultIfEmpty(0m).Sum();
}
from c in db.Clients
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = currentBalance.Invoke(c),
}).ToList();
由于未知的表达式错误而失败了:-
LINQ to Entities does not recognize the method 'System.Decimal Invoke[Client,Decimal](System.Linq.Expressions.Expression`1[System.Func`2[RPM.DAO.UI.Client,System.Decimal]], RPM.DAO.UI.Client)' method, and this method cannot be translated into a store expression
我也尝试过使用 Expand()
CurrentBalance = currentBalance.Expand().Invoke(c)
但仍然出现未知表达式错误。
只是为了看看,我尝试将 Sum 值默认为 0m,然后在将结果分配给 DTO 集合的循环中进行求和,如果需要的话
foreach (var client in Clients)
{
if (!ClientSearchExcludeCurrentBalance) {
var c = db.Clients.FirstOrDefault(cl => cl.ClientID == client.ClientID);
client.CurrentBalance = c.ClientTransactions.Select(ct => ct.fAmount)
.DefaultIfEmpty(0m).Sum();
}
}
这行得通,因为它只在被告知时才进行求和,但在主选择之外进行意味着整个查询现在需要的时间是过去的两倍,因此显然不可行。
所以,我的问题是:-
有谁知道是否可以让实体框架只运行将要使用的三元条件运算符的部分?
有谁知道是否可以使用表达式在实体框架中返回值?
或者,或者,如何将 IF 语句添加到实体框架查询中?
对于(非工作)示例:-
from c in db.Clients
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = if (ClientSearchExcludeCurrentBalance)
return 0m;
else
return c.ClientTransactions.Select(tf => tf.fAmount)
.DefaultIfEmpty(0m).Sum(),
}).ToList();
谢谢!
编辑:
我尝试了 Barr J 的解决方案:-
from c in db.Clients
let currentBalance = ClientSearchExcludeCurrentBalance ? 0m :
c.ClientTransactions.Select(ct => ct.Amount).DefaultIfEmpty(0m).Sum()
where {where clauses}
select new
{
ClientID = c.ClientID,
ClientName = c.ClientName,
CurrentBalance = currentBalance
}).ToList();
我得到一个空引用异常:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
编辑#2:上面的精简版没有给出空异常错误,但完整版(具有相同代码)确实......很奇怪!
无论如何,在上面的工作缩减版本中,我尝试将设置设置为 true 和 fall,并且两者都花费了相同的时间,所以它仍然以任何方式进行 Sum 评估
【问题讨论】:
-
不会改变查询形状,但会阻止不必要的数据读取:`c.ClientTransactions.Where(ct => ClientSearchExcludeCurrentBalance).Select(ct => ...)
标签: c# entity-framework expression ternary-operator