有没有一种简单的方法来附加 lambda 并重用 lambda 名称以创建我的 Linq where 条件?

发布于 2024-07-19 06:13:48 字数 591 浏览 5 评论 0 原文

我有一个用户控件,它接受一个 Func,然后将其提供给 IQueryable 的 Linq“Where”扩展方法。 这个想法是,从调用代码中,我可以传入所需的搜索函数。

我想动态地构建这个搜索函数:

Func<Order, bool> func == a => true;
if (txtName.Text.Length > 0) {
  //add it to the function
  func = a => func(a) && a.Name.StartsWith(txtName.Text);
}
if (txtType.Text.Length > 0) {
  //add it to the function
  func = a => func(a) && a.Type == txtType.Text;
}
..... etc .....

这种方法的问题是,由于我重复使用名称“func”,它创建了一个递归函数。

有没有一种简单的方法可以构建像这样的表达式树以创建动态 where 子句(在没有预先提供 IQueryable 并重复调用“Where”的情况下)?

I have a user control which takes a Func which it then gives to the Linq "Where" extension method of a IQueryable. The idea is that from the calling code, I can pass in the desired search function.

I'd like to build this search function dynamically as such:

Func<Order, bool> func == a => true;
if (txtName.Text.Length > 0) {
  //add it to the function
  func = a => func(a) && a.Name.StartsWith(txtName.Text);
}
if (txtType.Text.Length > 0) {
  //add it to the function
  func = a => func(a) && a.Type == txtType.Text;
}
..... etc .....

The problem with this approach is that since I'm reusing the name "func" it creates a recursive function.

Is there an easy way to build out the expression tree like this to make a dynamic where clause (in the absence of having the IQueryable up front and repeatedly calling "Where")?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

爺獨霸怡葒院 2024-07-26 06:13:49

只需将当前的 lambda 保存在临时变量中即可防止递归。

var tempFunc = func;
func = a => tempFunc(a) && ...

Just save the current lambda in a temporary variable to prevent recursion.

var tempFunc = func;
func = a => tempFunc(a) && ...
旧话新听 2024-07-26 06:13:49

如果您想要进行“and”组合,首选选项是使用多个“where”子句:

IQueryable<Order> query = ...
if (!string.IsNullOrEmpty(txtName.Text.Length)) {
  //add it to the function
  query = query.Where(a => a.Name.StartsWith(txtName.Text));
}
if (!string.IsNullOrEmpty(txtType.Text.Length)) {
  //add it to the function
  query = query.Where(a => a.Type == txtType.Text);
}

您可以通过表达式构建(AndAlso、Invoke 等)执行更复杂的操作,但这对于“and”来说不是必需的组合。

如果您确实需要组合表达式,那么方法取决于实现。 LINQ-to-SQL 和 LINQ-to-Objects 支持 Expression.Invoke,允许:

static Expression<Func<T, bool>> OrElse<T>(
    this Expression<Func<T, bool>> lhs,
    Expression<Func<T, bool>> rhs)
{
    var row = Expression.Parameter(typeof(T), "row");
    var body = Expression.OrElse(
        Expression.Invoke(lhs, row),
        Expression.Invoke(rhs, row));
    return Expression.Lambda<Func<T, bool>>(body, row);
}
static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> lhs,
    Expression<Func<T, bool>> rhs)
{
    var row = Expression.Parameter(typeof(T), "row");
    var body = Expression.AndAlso(
        Expression.Invoke(lhs, row),
        Expression.Invoke(rhs, row));
    return Expression.Lambda<Func<T, bool>>(body, row);
}

但是,对于实体框架,您通常需要将表达式拆开并重建它,这并不容易。 因此,为什么通常最好使用 Queryable.Where (表示“and”)和 Queryable.Concat (表示“or”)。

If you want to do an "and" combination, the preferred option is to use multiple "where" clauses:

IQueryable<Order> query = ...
if (!string.IsNullOrEmpty(txtName.Text.Length)) {
  //add it to the function
  query = query.Where(a => a.Name.StartsWith(txtName.Text));
}
if (!string.IsNullOrEmpty(txtType.Text.Length)) {
  //add it to the function
  query = query.Where(a => a.Type == txtType.Text);
}

You can do more complex things with expression building (AndAlso, Invoke, etc), but this is not necessary for an "and" combination.

If you really need to combine expressions, then the approach depends on the implementation. LINQ-to-SQL and LINQ-to-Objects support Expression.Invoke, allowing:

static Expression<Func<T, bool>> OrElse<T>(
    this Expression<Func<T, bool>> lhs,
    Expression<Func<T, bool>> rhs)
{
    var row = Expression.Parameter(typeof(T), "row");
    var body = Expression.OrElse(
        Expression.Invoke(lhs, row),
        Expression.Invoke(rhs, row));
    return Expression.Lambda<Func<T, bool>>(body, row);
}
static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> lhs,
    Expression<Func<T, bool>> rhs)
{
    var row = Expression.Parameter(typeof(T), "row");
    var body = Expression.AndAlso(
        Expression.Invoke(lhs, row),
        Expression.Invoke(rhs, row));
    return Expression.Lambda<Func<T, bool>>(body, row);
}

However, for Entity Framework you will usually need to rip the Expression apart and rebuild it, which is not easy. Hence why it is often preferable to use Queryable.Where (for "and") and Queryable.Concat (for "or").

东风软 2024-07-26 06:13:49

我正在做这件事...我正在使用表达式,因为 Func 是编译代码,其中 Expression> 可以转换为 C# 或 TSql 或其他...我刚刚看到几个人建议使用表达式而不仅仅是函数。

在您的搜索页面上,您将实现如下代码:

SearchCritera<Customer> crit = new SearchCriteria<Customer>();

if (txtName.Text.Length > 0) {
  //add it to the function
  crit.Add(a.Name.StartsWith(txtName.Text));
}

if (txtType.Text.Length > 0) {
  //add it to the function
  crit.Add(a.Type == txtType.Text));
}

SearchCriteria 对象看起来像这样...

public class SearchCritera<TEntity>
    {
        private List<Expression<Func<TEntity, bool>>> _Criteria = new List<Expression<Func<TEntity, bool>>>();

        public void Add(Expression<Func<TEntity, bool>> predicate)
        {
            _Criteria.Add(predicate);
        }

        // This is where your list of Expression get built into a single Expression 
        // to use in your Where clause
        public Expression<Func<TEntity, bool>> BuildWhereExpression()
        {
            Expression<Func<TEntity, bool>> result = default(Expression<Func<TEntity, bool>>);
            ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "entity");
            Expression previous = _Criteria[0];

            for (int i = 1; i < _Criteria.Count; i++)
            {
                previous = Expression.And(previous, _Criteria[i]);
            }

            result = Expression.Lambda<Func<TEntity, bool>>(previous, parameter);

            return result;
        }
    }

然后从您的Where 子句中您可以执行此操作...

public List<Customer> FindAllCustomers(SearchCriteria criteria)
{
   return LinqToSqlDataContext.Customers.Where(SearchCriteria.BuildWhereExpression()).ToList();
}

这是我第一次对此进行编码,您可能需要编写出于您的目的进行一些更改,我知道它可以编译,但是当我真正去做时,我会对它进行单元测试,但这是我一直在脑海中徘徊的想法......

I am right in the middle of doing exactly this... I am using Expressions because Func is compiled code where as Expression<Func<YourObect, boo>> can be converted C# or TSql or what ever... I just have seen several people recommend using expression instead of just func.

On you search page you would implement the code like this:

SearchCritera<Customer> crit = new SearchCriteria<Customer>();

if (txtName.Text.Length > 0) {
  //add it to the function
  crit.Add(a.Name.StartsWith(txtName.Text));
}

if (txtType.Text.Length > 0) {
  //add it to the function
  crit.Add(a.Type == txtType.Text));
}

The SearchCriteria object look something like this...

public class SearchCritera<TEntity>
    {
        private List<Expression<Func<TEntity, bool>>> _Criteria = new List<Expression<Func<TEntity, bool>>>();

        public void Add(Expression<Func<TEntity, bool>> predicate)
        {
            _Criteria.Add(predicate);
        }

        // This is where your list of Expression get built into a single Expression 
        // to use in your Where clause
        public Expression<Func<TEntity, bool>> BuildWhereExpression()
        {
            Expression<Func<TEntity, bool>> result = default(Expression<Func<TEntity, bool>>);
            ParameterExpression parameter = Expression.Parameter(typeof(TEntity), "entity");
            Expression previous = _Criteria[0];

            for (int i = 1; i < _Criteria.Count; i++)
            {
                previous = Expression.And(previous, _Criteria[i]);
            }

            result = Expression.Lambda<Func<TEntity, bool>>(previous, parameter);

            return result;
        }
    }

Then from your Where clause you could do this...

public List<Customer> FindAllCustomers(SearchCriteria criteria)
{
   return LinqToSqlDataContext.Customers.Where(SearchCriteria.BuildWhereExpression()).ToList();
}

This is the first time I have coded this out and you might need to make some changes for your purposes, I know it compliles but when I acutally go to do it I will unit test it, but it is the idea I have been tossing around in my head...

愚人国度 2024-07-26 06:13:49

如果您要在 LinqToSQL 或任何其他动态表达式树解析器中使用它,您将需要使用 PredicateBuilder!!!

否则:

此扩展方法将阻止递归:

    public static Func<T, bool> And<T>(this Func<T, bool> f1, Func<T, bool> f2)
    {
        return a => f1(a) && f2(a);
    }

    public static Func<T, bool> Or<T>(this Func<T, bool> f1, Func<T, bool> f2)
    {
        return a => f1(a) || f2(a);
    }

像这样使用它:

Func<Order, bool> func == a => true;
if (txtName.Text.Length > 0) {
  //add it to the function
  func.And(a => a.Name.StartsWith(txtName.Text));
}
if (txtType.Text.Length > 0) {
  //add it to the function
  func.And(a => a.Type == txtType.Text);
}

If you're going to be using this in LinqToSQL or any other dynamic expression tree parser, you're going to want to use PredicateBuilder!!!

Otherwise:

This extension method will prevent the recursion:

    public static Func<T, bool> And<T>(this Func<T, bool> f1, Func<T, bool> f2)
    {
        return a => f1(a) && f2(a);
    }

    public static Func<T, bool> Or<T>(this Func<T, bool> f1, Func<T, bool> f2)
    {
        return a => f1(a) || f2(a);
    }

Use it like so:

Func<Order, bool> func == a => true;
if (txtName.Text.Length > 0) {
  //add it to the function
  func.And(a => a.Name.StartsWith(txtName.Text));
}
if (txtType.Text.Length > 0) {
  //add it to the function
  func.And(a => a.Type == txtType.Text);
}
悲喜皆因你 2024-07-26 06:13:49
Dictionary<Func<bool>, Expression<Func<Order, bool>>> filters =
  new Dictionary<Func<bool>, Expression<Func<Order, bool>>>();
// add a name filter
filters.Add(
  () => txtName.Text.Length > 0,
  a => a.Name.StartsWith(txtName.Text)
);
// add a type filter
filters.Add(
  () => txtType.Text.Length > 0,
  a => a.Type == txtType.Text
);

...

var query = dc.Orders.AsQueryable();

foreach( var filter in filters
  .Where(kvp => kvp.Key())
  .Select(kvp => kvp.Value) )
{
  var inScopeFilter = filter;
  query = query.Where(inScopeFilter);
}
Dictionary<Func<bool>, Expression<Func<Order, bool>>> filters =
  new Dictionary<Func<bool>, Expression<Func<Order, bool>>>();
// add a name filter
filters.Add(
  () => txtName.Text.Length > 0,
  a => a.Name.StartsWith(txtName.Text)
);
// add a type filter
filters.Add(
  () => txtType.Text.Length > 0,
  a => a.Type == txtType.Text
);

...

var query = dc.Orders.AsQueryable();

foreach( var filter in filters
  .Where(kvp => kvp.Key())
  .Select(kvp => kvp.Value) )
{
  var inScopeFilter = filter;
  query = query.Where(inScopeFilter);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文