如何在运行时使用不同类型的多个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方面的专家,但我有一种感觉,我想要的可能是可能的。如果我在这里做了愚蠢的事情,请指出来,我会改正的。
我不这么认为。至少使用构建表达式的方法。至少我最终为每个类型构建了表达式。主要原因是
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