如何在运行时使用不同类型的多个group by子句构建linq查询?

本文关键字:by group 子句 构建 查询 linq 运行时 同类型 | 更新日期: 2023-09-27 18:11:11

我的情况是,我需要在运行时对ef上下文指定查询。我们的顾问在客户站点为客户特定的情况配置这些查询。

为了方便起见,我正在考虑使用linq来构建查询,基于顾问在某种前端指定的标准列表(现在是winforms)。咨询器基本上是从对象指定属性,指定操作符,然后指定值。例如:给我所有[status][等于][1]的客户端。

目前,我有一个表达式构建器,它在运行时创建where子句,到目前为止,我可以按子句管理一个组。当顾问配置不同类型的子句(例如字符串和datetime属性)的多个组时,我在这里碰壁。

例如,我必须能够处理以下查询:从状态= 1的客户端中选择bsn为a, dateofbirth为b(其中bsn = string, dateofbirth = datetime)。

目前,这是将查询"粘合"在一起的代码:

public List<ClientV2> ExportClients(List<CriteriaV2> criteriaList)
{
    var whereExpression = BuildWhereExpressionChain(criteriaList.Where(c => c.Operator != CriteriaOperatorV2.GROUPBY).ToList());
    var groupByExpression = BuildGroupByExpression(criteriaList.Where(c => c.Operator == CriteriaOperatorV2.GROUPBY).ToList());
    var sourceClients = _context.Clients.Where(whereExpression).GroupBy(groupByExpression).ToList();
    IEnumerable<Client> resultClients = sourceClients.SelectMany(group => group);
    return ClientToClientV2.MapList(resultClients.ToList());
}

where子句构造器:

private Expression<Func<Client, bool>> BuildWhereExpressionChain(List<CriteriaV2> criteriaList)
{
    var expressionList = new List<Expression<Func<Client, bool>>>();
    var paramExp = Expression.Parameter(typeof(Client));
    foreach (var crit in criteriaList)
    {
        var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property);
        if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als filter criterium");
        var propInfo = typeof(Client).GetProperty(propertyItem.InternalName);
        var left = Expression.Property(paramExp, propInfo);
        Expression right;
        if (propInfo.PropertyType.IsEnum)
            right = Expression.Constant(Enum.ToObject(propInfo.PropertyType, PropertyTranslator.TranslateEnum(propertyItem.Type, crit.Value)));
        else if (propInfo.PropertyType == typeof(DateTime) || propInfo.PropertyType == typeof(DateTime?))
            right = Expression.Constant(DateTime.Parse(crit.Value), propInfo.PropertyType);
        else
            right = Expression.Constant(crit.Value, typeof(string));
        var exp = BuildExpression(left, right, crit.Operator);
        expressionList.Add(Expression.Lambda<Func<Client, bool>>(exp, new ParameterExpression[] { paramExp }));
    }
    var firstExpression = expressionList.First();
    expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); });
    return firstExpression;
}

这是我卡住的部分(它确实适用于字符串类型的一个子句):

private Expression<Func<Client, string>> BuildGroupByExpression(List<CriteriaV2> criteriaList)
{
    var expressionList = new List<Expression<Func<Client, string>>>();
    var paramExp = Expression.Parameter(typeof(Client));
    foreach (var crit in criteriaList)
    {
        var propertyItem = PropertyTranslator.GetPropertyItem(crit.Property);
        if (propertyItem == null) throw new InvalidFilterCriteriaException("Property " + crit.Property + " niet toegestaan als group by criterium");
        var propInfo = typeof(Client).GetProperty(propertyItem.InternalName);
        var body = Expression.Property(paramExp, propInfo);
        var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp);
        expressionList.Add(lambda);
    }
    var firstExpression = expressionList.First();
    expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.And(ex); });
    return firstExpression;
}

是否有可能使BuildGroupByExpression()以这样的方式产生一个包含不同类型的多个子句的表达式,我可以直接在.GroupBy(expression)中使用?

我不是linq方面的专家,但我有一种感觉,我想要的可能是可能的。如果我在这里做了愚蠢的事情,请指出来,我会改正的。

如何在运行时使用不同类型的多个group by子句构建linq查询?

我不这么认为。至少使用构建表达式的方法。至少我最终为每个类型构建了表达式。主要原因是

  var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp);

我不知道如何使这个Lambda定义动态或泛型。

有一种不同的方法,一个库在运行时使用字符串解释来构建表达式。看到

Install-Package System.Linq.Dynamic.Library

参见https://msdn.microsoft.com/en-US/vstudio/bb894665.aspx