【问题标题】:Build a query using dynamic where condition and a dynamic list使用动态 where 条件和动态列表构建查询
【发布时间】:2015-01-30 22:33:01
【问题描述】:

我的扩展方法如下:

public static IQueryable<TSource> TrialBatch<TSource>(this IQueryable<TSource> sourceQuery, List<long> Ids, Expression<Func<TSource, object>> expression)
{
    // Expected Code
}

扩展方法中接收的表达式变量如下"x => x.EmployeeID"

是否可以如下转换表达式?

"x => Ids.Contains(x.EmployeeID)" 这样我们就可以将它与 'sourceQuery' 结合起来并返回相同的结果。

这类似于 dbContext.EmployeeIDDetails.Where(x => Ids.Contains(x.EmployeeID)).ToList(); 唯一的区别是我们将动态发送 Id 和 where condition("x => x.EmployeeID") 到表。

我将这种类型的扩展方法用于开发目的,我也很好奇这是否可行。如果您有任何疑问,请添加评论

提前致谢。

【问题讨论】:

  • expression 应该是 Expression&lt;Func&lt;TSource, long&gt;&gt; 如果您希望确定该选择器的结果是否在列表中。或者更好的是,它应该使用第二个通用参数。

标签: c# linq entity-framework dynamic iqueryable


【解决方案1】:

使用LINQKit 允许选择器在另一个表达式中展开:

public static IQueryable<TSource> WhereIn<TSource, TProp>(
    this IQueryable<TSource> query,
    IEnumerable<TProp> list,
    Expression<Func<TSource, TProp>> selector)
{
    return query.AsExpandable()
        .Where(item => list.Contains(selector.Invoke(item)));
}

如果你不想使用 LinqKit,你可以编写自己的方法来组合表达式。

Compose 方法很简单,只需将组合方法的参数的所有实例替换为组合方法的主体即可:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

这使用以下方法将表达式的所有实例替换为另一个:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

现在你可以写了:

public static IQueryable<TSource> WhereIn<TSource, TProp>(
    this IQueryable<TSource> query,
    IEnumerable<TProp> sequence,
    Expression<Func<TSource, TProp>> selector)
{
    return query.Where(selector.Compose(value => sequence.Contains(value)));
}

【讨论】:

  • 正是我要回答的问题。虽然我会用IEnumerable&lt;TProp&gt; 替换List&lt;TProp&gt;
  • @Servy 非常感谢。这真的很有帮助。
  • 嗨@Servy 是否可以在不使用 LINQKit 的情况下实现相同的功能?
  • @JagdshLK 当然,我们可以编写一些帮助方法来让我们在这里做同样的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
相关资源
最近更新 更多