c# - 在 Entity Framework 中使用三元条件运算符或表达式

标签 c# entity-framework expression ternary-operator

我有一个我继承的 Entity Framework 查询,其中包括几个总和,缩减示例:-

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();

随着客户端数量和交易数量的增长,这个查询明显变得越来越慢。

理想情况下,我希望存储余额而不是每次都计算余额,但目前系统不会这样做,而且实现起来会是一个非常大的变化,所以现在我只是尝试一个 band-帮助修复。

我尝试实现的修复是简单地不为对它们不感兴趣的人进行 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();
    }
}

这是有效的,因为它只在被告知时才求和,但在主选择之外进行求和意味着整个查询现在花费的时间是以前的两倍,因此显然不可行。

所以,我的问题是:-

有谁知道是否可以让 Entity Framework 只运行将要使用的三元条件运算符的部分?

有谁知道在 Entity Framework 中是否可以使用表达式返回值?

或者,如何将 IF 语句添加到 Entity Framework 查询中?

对于(非工作)示例:-

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:上面的精简版没有给出 null 异常错误,但是完整版(具有相同的代码)确实...很奇怪!

无论如何,对于上面的工作缩减版本,我尝试将设置设置为 true 和 fall,并且两者都花费了相同的时间,所以它仍然以任何一种方式进行 Sum 评估

最佳答案

无论三元运算符如何,Linq 都会从两边计算操作数,因为它是在运行时计算的

您必须评估 linq 语句外部的操作数,然后使用它。

例如:

var tst = from p in products join i in info on p.id equals i.pid

let status = p.type = "home" ? homestatus.Select(s=>s.status) :
             p.type = "offshore" ? offshorestatus.Select(s=>s.status) :
             p.type = "internal" ? internalestatus.Select(s=>s.status) : null
select new {
name = p.name,
status = status != null ? status.StatusText : string.Empty;
}

或:

var tst = from p in products join i in info on p.id equals i.pid

let status = (p.type = "home" ? homestatus.Select(s=>s.status.StatusText) :
             p.type = "offshore" ? offshorestatus.Select(s=>s.status.StatusText) :
             p.type = "internal" ? internalestatus.Select(s=>s.status.StatusText) : null) ?? string.Empty
select new {
name = p.name,
status = status;
}

关于c# - 在 Entity Framework 中使用三元条件运算符或表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51422141/

相关文章:

c# - 如何创建值为模型属性的 TagHelper(不使用@Model)?

c# - 如果参数为空则排除条件

regex - 具有所有属性的 anchor 标记的正则表达式

c# - 任务栏通知发光

c# - 在 ASP.NET Core 中将 Razor View 渲染为字符串

c# - 在 LINQ 中获取组的第一条记录?

expression - (x<<13) ^ x 是什么意思?

java - C和JAVA中自增自减运算符的区别

c# - 我可以克隆一个 IQueryable 以在另一个 DbContext 的 DbSet 上运行吗?

c# - 有什么充分的理由不依赖业务逻辑中的数据库 key 完整性吗?