如何将列表中字段的值与Where子句中另一个列表中的另一个值进行比较

时间:2019-02-12 00:27:39

标签: c# entity-framework

我有一个“员工评估”表的列表,该表具有TotalResult字段,此字段中的值介于1到10之间。另一个表“结果细分”具有以下列:

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%

现在,用户具有“费率细分”表的多选列表

enter image description here

如果用户选择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表中的记录
  • query是EmployeeAppraisal
  • 的可查询对象

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

因为它的作用类似于“ And”,所以它的作用应类似于“ OR”,但我不知道该怎么写。

2 个答案:

答案 0 :(得分:0)

这是让我烦恼了一段时间的问题,这个问题促使我稍微深究一下。 .Where()将使用AndAlso操作附加条件,但正如您指出的那样。为了使EF和Linq更动态地支持OrElse条件,您需要将条件树或条件一起重建一点。恭敬user743382在Exception using OrElse and AndAlso expression methods

上的回答

您将需要几个类,以使表达式访问者可以将要与多个表达式组合在一起的参数。像这样:

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可以将它们正确地评估为单个表达式。

答案 1 :(得分:0)

交叉连接可以是以下一种解决方案:

    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;