【问题标题】:How to compare value of a field in a list to another value in another list in Where clause如何将列表中字段的值与Where子句中另一个列表中的另一个值进行比较
【发布时间】:2019-02-12 00:27:39
【问题描述】:

我有一个Employee Appraisal 表的列表,这个表有TotalResult 字段,这个字段的值在1 到10 之间。另一个表Result Segmentation 有以下列:

Id int, Max double, Min double, Desc string

假设我有这些用于员工评估的数据:​​

EmpId  EmpName   TotalResult
---  -------   -----------
1      Jaims     1.5
2      Johny     8.3
3      Moon      5.6
4      Michle    7
5      Mariam    9
6      Kamel     4

结果分割值

Id   Max    Min   Desc
---  ---    ---   -----
1     3      1     ~ 30%
2     4      3     40%
3     5      4     50%
4     6      5     60%
5     7      6     70%
6     10     7     ~ 80%

现在,用户有一个 Rate Segmentation 表的多选列表

如果用户选择 70% 和 40%,查询应该显示这些员工评价:

EmpId  EmpName   TotalResult
-----  -------   -----------
3      Moon      5.6
6      Kamel     4
4      Michle    7

我写了这段代码

if (rateSegIds != null)
{
    var rateSegs = _repositoryRateSeg.Query(x => rateSegId.Contains(x.Id)).ToList();

    if (rateSeg.Any())
    {
        foreach (var segmentation in rateSeg)
        {
            query = query.Where(x => x.TotalResult > segmentation.Min &&  x.TotalResult <= segmentation.Max);       
        }
    }
}
  • rateSegIds 是保存用户选择的整数列表
  • rateSegs 包含根据 Id 列表来自 RateSegmataions 表的记录
  • 查询是EmployeeAppraisal表的可查询对象

此代码仅在用户从列表中选择一个值时有效,如果他/她选择多个值,则查询将不返回任何内容。

因为它表现得像 "And" ,它应该表现得像 "OR" 但我不知道怎么写。

【问题讨论】:

标签: c# entity-framework


【解决方案1】:

这是前段时间一直困扰我的事情,这个问题只是促使我深入研究它。 .Where() 将附加条件,但正如您所指出的,使用 AndAlso 操作。要让 EF 和 Linq 更动态地支持 OrElse 条件,您需要稍微重建表达式树或一起重建条件。感谢 user743382 在Exception using OrElse and AndAlso expression methods 上的回答

您将需要几个类来使表达式访问者能够将多个表达式的参数排列在一起以进行 Or'd 运算。比如:

private Expression<Func<EmployeeAppraisal, bool>> buildFilterExpression(IEnumerable<Segment> segments)
{
    Expression<Func<EmployeeAppraisal, bool>> exp = c => false;

    foreach (var segment in segments)
    {
        Expression<Func<EmployeeAppraisal, bool>> filter = x => x.TotalResult >= segment.Min && x.TotalResult <= segment.Max;
        exp = Expression.Lambda<Func<EmployeeAppraisal, bool>>(Expression.OrElse(exp.Body,
            new ExpressionParameterReplacer(filter.Parameters, exp.Parameters).Visit(filter.Body)), exp.Parameters);
    }

    return exp;
}

private class ExpressionParameterReplacer : ExpressionVisitor
{
    public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
    {
        ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
        for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
            ParameterReplacements.Add(fromParameters[i], toParameters[i]);
    }

    private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements
    {
        get;
        set;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        ParameterExpression replacement;
        if (ParameterReplacements.TryGetValue(node, out replacement))
            node = replacement;
        return base.VisitParameter(node);
    }
} 

然后在你的 EF Linq 表达式中:

var rateSegs = _repositoryRateSeg.Query(x => rateSegId.Contains(x.Id)).ToList();

if (rateSeg.Any())
    query = query.Where(buildFilterExpression(rateSegs));

ExpressionParameterReplacer 和支持类将不同的表达式主体进行或运算,并确保它们关联到相同的表达式参数,以便 Linq 将它们作为单个表达式正确评估。

【讨论】:

    【解决方案2】:

    交叉连接可以是一种解决方案,例如:

        var rateSegIds = new int[] {2, 5}; //40% and 70%
    
        var result = from emp in EmployeeAppraisals
                     from segment in Segments.Where(x => rateSegIds.Contains(x.Id))
                     where emp.Total >= segment.Min && emp.Total <= segment.Max
                     select emp;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-30
      相关资源
      最近更新 更多