我有一个系统,允许将与销售有关的不同条件存储在数据库中。加载标准时,它们用于构建查询并返回所有适用的销售。该标准的对象是这样的:使用表达式树在循环中构建动态查询
ReferenceColumn(在他们申请销售表中的列)
MINVALUE(最低值的参考列必须)
的MaxValue(最大值参考列必须是)
搜索销售是使用上述标准的集合完成的。相同类型的ReferenceColumns一起进行OR运算,不同类型的ReferenceColumns一起进行AND运算。因此,举例来说,如果我有三个标准:
ReferenceColumn: '价格',MINVALUE: '10',MaxValue的: '20'
ReferenceColumn: '价格',MINVALUE: '80后',MaxValue的:“100 “
ReferenceColumn: '年龄',MINVALUE: '2',MaxValue的: '3'
查询应返回所有销售那里的价格是10-20之间或80-100之间,但所销售年龄在2至3岁之间。
我把它实现使用SQL查询字符串,并使用执行.FromSql:
public IEnumerable<Sale> GetByCriteria(ICollection<SaleCriteria> criteria)
{
StringBuilder sb = new StringBuilder("SELECT * FROM Sale");
var referenceFields = criteria.GroupBy(c => c.ReferenceColumn);
// Adding this at the start so we can always append " AND..." to each outer iteration
if (referenceFields.Count() > 0)
{
sb.Append(" WHERE 1 = 1");
}
// AND all iterations here together
foreach (IGrouping<string, SaleCriteria> criteriaGrouping in referenceFields)
{
// So we can always use " OR..."
sb.Append(" AND (1 = 0");
// OR all iterations here together
foreach (SaleCriteria sc in criteriaGrouping)
{
sb.Append($" OR {sc.ReferenceColumn} BETWEEN '{sc.MinValue}' AND '{sc.MaxValue}'");
}
sb.Append(")");
}
return _context.Sale.FromSql(sb.ToString();
}
而这其实工作只是与我们的数据库很好,但它没有发挥好与其他收藏品,格外的InMemory数据库我们用于UnitTesting,所以我试图用表达式树重写它,这是我以前从未使用过的。到目前为止,我已经得到了这个:
public IEnumerable<Sale> GetByCriteria(ICollection<SaleCriteria> criteria)
{
var referenceFields = criteria.GroupBy(c => c.ReferenceColumn);
Expression masterExpression = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
List<ParameterExpression> parameters = new List<ParameterExpression>();
// AND these...
foreach (IGrouping<string, SaleCriteria> criteriaGrouping in referenceFields)
{
Expression innerExpression = Expression.Equal(Expression.Constant(1), Expression.Constant(0));
ParameterExpression referenceColumn = Expression.Parameter(typeof(Decimal), criteriaGrouping.Key);
parameters.Add(referenceColumn);
// OR these...
foreach (SaleCriteria sc in criteriaGrouping)
{
Expression low = Expression.Constant(Decimal.Parse(sc.MinValue));
Expression high = Expression.Constant(Decimal.Parse(sc.MaxValue));
Expression rangeExpression = Expression.GreaterThanOrEqual(referenceColumn, low);
rangeExpression = Expression.AndAlso(rangeExpression, Expression.LessThanOrEqual(referenceColumn, high));
innerExpression = Expression.OrElse(masterExpression, rangeExpression);
}
masterExpression = Expression.AndAlso(masterExpression, innerExpression);
}
var lamda = Expression.Lambda<Func<Sale, bool>>(masterExpression, parameters);
return _context.Sale.Where(lamda.Compile());
}
当我调用Expression.Lamda时,它目前正在抛出一个ArgumentException。 Decimal不能在那里使用,它表示它想要销售类型,但我不知道该在哪里销售,我不确定我在这里的正确轨道上。我也担心我的masterExpression每次都会自我复制,而不是像字符串构建器那样追加,但也许这样做会起作用。
我正在寻找关于如何将此动态查询转换为表达式树的帮助,如果我在此处建立基础,我可以采用完全不同的方法。
剂量的原代码的工作?这不应该工作,为什么你使用1 = 1和1 = 0? –
是的,如果集合是使用SQL Server的DbContext的一部分,它就可以工作。 1 = 1和1 = 0,所以我总是可以追加'AND'/'OR'到查询字符串,而不必处理第一次迭代特例等。 – Valuator
尝试使用LINQKit(http://www.albahari .com/nutshell/linqkit.aspx),它使它更容易。该页面说:使用LINQKit,您可以:...动态构建谓词 – Tom