我需要在运行时在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的专家不是那么多,但我觉得我想要的是可能的。如果我在这里做了愚蠢的事情,请指出它们,我将继续努力。
答案 0 :(得分:0)
我不这么认为。至少使用表达式构建方法,你可以使用它。至少我最终建立了每种类型的表达式。 主要原因是
var lambda = Expression.Lambda<Func<Client, string>>(body, paramExp);
我不知道如何使这个Lambda定义动态或通用。
有一种不同的方法,以及一个在运行时使用字符串解释来构建表达式的库。见
Install-Package System.Linq.Dynamic.Library