如何在运行时使用具有不同类型的多个group by子句

时间:2015-09-03 10:29:39

标签: c# .net linq entity-framework

我需要在运行时在EF上下文中指定查询。我们的顾问会根据客户的具体情况在客户现场配置这些查询。

为了便于我考虑使用linq构建查询,基于顾问在某种前端指定的条件列表(现在,winforms)。顾问基本上从对象指定属性,指定运算符,然后指定值。例如:给我所有[status] [等于] [1]的客户。

目前我有一个表达式构建器,它在运行时创建了where子句,到目前为止,我可以管理一个group by子句。我在这里跑墙的时候,是一个顾问配置多个不同类型的子句组(例如字符串和日期时间属性)。

例如,我必须能够处理此查询:选择bsn作为a,dateofbirth为b,来自客户,其中status = 1 group by bsn,dateofbirth(其中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的专家不是那么多,但我觉得我想要的是可能的。如果我在这里做了愚蠢的事情,请指出它们,我将继续努力。

1 个答案:

答案 0 :(得分:0)

我不这么认为。至少使用表达式构建方法,你可以使用它。至少我最终建立了每种类型的表达式。 主要原因是

  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