【问题标题】:conditional where in linq querylinq 查询中的条件位置
【发布时间】:2020-02-20 23:44:51
【问题描述】:

我想从 linq 查询中返回总和,我传入了 2 个可能/可能不包含在查询中的参数。

OrgId - 整数

reportType - 整数

那么两个问题:

  1. 如何更新下面的查询,以便如果 OrgId = 0 则忽略组织字段(全部返回)?

  2. LocumClaimTF 可以是 True/False/Both,如果两者都忽略此字段的 where 查询。

这是我到目前为止所做的,这是有效的,但我想要一些高效的东西。

            // Using reportType set preferences for LocumClaimTF
            bool locumClaimTF1, locumClaimTF2 = false;
            if (reportType == 0)
            {
                locumClaimTF1 = false;
                locumClaimTF2 = false;
            }
            else if (reportType == 1)
            {
                locumClaimTF1 = true;
                locumClaimTF2 = true;
            }
            else // 2
            {
                locumClaimTF1 = true;
                locumClaimTF2 = false;
            }

            if (OrgID != 0) // Get by OrgID
            {
                return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
             .Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
             .Where(x => x.Shift.organisationID == OrgID)
             .GroupBy(s => s.assignedLocumID)
                 .Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
                ).Sum(g=>g.dataCurrencyAmount);
            }
            else // Ignore OrgID - Get ALL Orgs
            {
                return _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate)
               .Where(x => x.Shift.LocumClaimTF == locumClaimTF1 || x.Shift.LocumClaimTF == locumClaimTF2)
               .GroupBy(s => s.assignedLocumID)
                  .Select(g => new dataRowDTO { dataLabel = string.Concat(g.FirstOrDefault().User.FullName), dataCount = g.Count(), dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) }
                  ).Sum(g => g.dataCurrencyAmount);
            }

我正在使用带有工作单元模式的 EF 来获取数据文件

【问题讨论】:

    标签: entity-framework linq linq-to-entities


    【解决方案1】:

    从上到下想到了几件事:

    用于处理布尔值

    bool locumClaimTF1 = (reportType == 1 || reportType == 2);
    bool locumClaimTF2 = (reportType == 1);
    

    从我在查询中看到的内容来看,如果报告类型是 1 或 2,您希望 Shift 的 LocumClaimTF 标志必须为 True。如果是这种情况,那么您可以忘记布尔标志并在您的条件下使用 reportType。

    接下来,为了编写查询,您可以有条件地编写 where 子句。这是流畅的 Linq 语法的一个优点。但是,让我们暂时从常规 DbContext 而不是 UoW 开始,因为这会引入一些您需要查看的复杂性和问题。 (我将在下面介绍)

    ​​>
    using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
    {
        var query = context.ShiftDates
           .Where(x => x.shiftStartDate >= StartDate 
               && x.shiftEndDate <= EndDate);
        if (reportType == 1 || reportType == 2)
           query = query.Where(x.Shift.LocumClaimTF);
        if (OrgId > 0)
           query = query.Where(x => x.Shift.organisationID == OrgID);
    
        var total = query.GroupBy(s => s.assignedLocumID)
            .Select(g => new dataRowDTO 
            { 
               dataLabel = tring.Concat(g.FirstOrDefault().User.FullName), 
               dataCount = g.Count(), 
               dataCurrencyAmount = g.Sum(sd => sd.shiftDateTotal.Value) 
            })
            .Sum(g=>g.dataCurrencyAmount);
    }
    

    现在这里没有任何意义。你为什么要对数据进行分组、计数和求和,只是为了求和?我怀疑您复制了一个为分组结果选择 DTO 的现有查询。如果您不需要分组结果,则只需要总数。所以在这种情况下,取消分组,只取所有适用记录的总和:

        var total = query.Sum(x => x.shiftDateTotal.Value);
    

    所以整个事情看起来像:

    using (var context = new ApplicationDbContext()) // <- insert your DbContext here...
    {
        var query = context.ShiftDates
           .Where(x => x.shiftStartDate >= StartDate 
               && x.shiftEndDate <= EndDate);
        if (reportType == 1 || reportType == 2)
           query = query.Where(x.Shift.LocumClaimTF);
        if (OrgId > 0)
           query = query.Where(x => x.Shift.organisationID == OrgID);
    
        var total = query.Sum(x => x.shiftDateTotal.Value);
        return total;
    }
    

    回到工作单元:使用此模式时的主要考虑因素是确保此Get 调用绝对必须返回IQueryable&lt;TEntity&gt;。如果它返回任何其他内容,例如IEnumerable&lt;TEntity&gt;,那么您将面临重大的性能问题,因为它将返回加载到内存的实体列表,而不是您可以扩展以构建对数据库的高效查询的东西。如果Get 方法没有返回IQueryable,或者在其中的任何地方都包含ToList 等方法,然后是AsQueryable(),那么请与开发团队的其他成员交谈,因为您实际上是站在代码上/EF 相当于地雷。如果它确实返回IQueryable&lt;TEntity&gt;(在这种情况下为IQueryable&lt;ShiftDate&gt;),那么您可以将其替换回上述查询:

    var query = _UoW.ShiftDates.Get(x => x.shiftStartDate >= StartDate && x.shiftEndDate <= EndDate);
    if (reportType == 1 || reportType == 2)
       query = query.Where(x.Shift.LocumClaimTF);
    if (OrgId > 0)
       query = query.Where(x => x.Shift.organisationID == OrgID);
    
    var total = query.Sum(x => x.shiftDateTotal.Value);
    return total;
    

    【讨论】:

    • 谢谢你,我已经完全把这些查询复杂化了。我的问题是 UoW 使用的是 IQueryable,但它在将其发送回控制器之前将其转换为 ToList。我们现在已经为此类查询向 UoW 添加了 IQueryable 方法。
    • 很高兴听到。 EF 是一个很棒的库,但它可以让你发现类似这样的性能陷阱。它们可能不会在低负载下(例如在开发过程中)表现出来,但在生产过程中会抬起头来。有帮助的一件事是习惯于在测试代码时针对数据库运行分析器。捕获在一个简短的测试场景中运行的 SQL 语句列表并检查查询。确保每个都是合理的,并寻找繁重的查询或正在运行的无法解释的查询数量。
    猜你喜欢
    • 2014-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多