将Where从LINQ子句转换为动态LINQ

本文关键字:LINQ 动态 转换 子句 Where | 更新日期: 2023-09-27 18:28:46

我想从

        var selectData = (from i in data
                          where i.Name == "Bob1"
                          select i);

        var selectData = (from i in data
                          select i).Where("Name==Bob1");

我尝试了各种方法(AsQueryableWhere<SomeData>),但无法获得要编译的第二个表单。

我对C#的通用扩展方法没有很好的理解。<Tsource>对我来说没有意义,所以这可能是问题所在。此外,我不明白为什么我可以在intellisense只显示.Where<>(泛型)的情况下键入.Where()。我希望看到第二个没有通用符号的Where。。。唉,我没有。

public class SomeData
{
    public string Name { get; set; }
    public string Address { get; set; }
}

更新
对于如何使用Where(),似乎有一些困惑,这很可能是我的错。请参阅相关问题。基于这个答案,where子句中的财产名称是完全合法的。我需要该属性保持字符串。如果这意味着需要动态LINQ,那就顺其自然吧…这就是我所需要的。

将Where从LINQ子句转换为动态LINQ

var selectData = (from i in data
                  select i).Where(datum => datum.Name == "Bob1");

Where方法接受委托,而不是字符串,因此需要传入委托或lambda。

编辑:根据您对其他答案之一的评论,您需要使用"反射"使属性值查找动态。

编辑:看起来您需要单独下载DynamicLinq库的源代码。

在您的帮助下,我成功地将转换为函数。

  1. 安装动态LINQ(我使用NUGET.Search在线搜索System.LINQ.Dynamic)
  2. 添加using System.Linq.Dynamic
  3. 查询应采用形式

        var selectData = (from i in data
                          select i).AsQueryable().Where("Name = @0","Bob1");//@0 is called an identifier.  "Name = Bob1" straight up fails.
    
  4. 安装ScottGU的C#示例库。。。这很有帮助。(VB)(原帖)

更新

起初我误解了这个问题;这个问题的解决方案是下载DynamicLinq并引用它。我将在下面留下我的答案,它解决了您关于通用扩展方法的附带问题。


var selectData = (from i in data 
    select i).Where(d => d.Name=="Bob1");

但为什么不这样呢:

var selectData = data.Where(d => d.Name=="Bob1");

关于where的"非通用"版本,没有这样的东西。在上面的调用中,泛型方法的类型参数是隐式的;它是由编译器推断出来的,它编译调用的方式与编译以下内容完全相同:

var selectData = data.Where<SomeData>(d => d.Name=="Bob1");

也许Where方法的草图实现将有助于减少您对TSource参数的困惑:

public static IEnumerable<TSource> Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    foreach (TSource item in source)
        if (predicate(item))
            yield return item;
}

TSource是您正在查询的序列的元素类型。它也是结果序列的元素类型。

编译器需要知道类型至少有两个原因:

首先,我们需要对每个元素调用一个函数,以确定是否将其包含在结果序列中。编译器必须知道predicate参数的引用可以安全地接受TSource类型的参数。

在这种情况下,第二个原因有些微不足道;item必须与TSource的赋值兼容,因为它在yield return语句中使用。当然,它是兼容的,因为它是同一类型的。

我相信这就是您想要的:

http://www.albahari.com/nutshell/predicatebuilder.aspx

示例

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();
  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}

源代码

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
public static class PredicateBuilder
{
  public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
  public static Expression<Func<T, bool>> False<T> () { return f => false; }
  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }
  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}

下面是一些简单的代码,使用表达式树来执行您想要的操作。这只适用于针对该特定类型的Property=="…"查询。当然,您可以修改它,并根据需要使其通用。

    public void Test()
    {
        List<SomeData> data = new List<SomeData>();
        data.Add(new SomeData("Mark", "Ledgewood Drive"));
        data.Add(new SomeData("Tim", "Sumpter Drive"));
        data.Add(new SomeData("Sean", "Leroy Drive"));
        data.Add(new SomeData("Bob", "Wilmington Road"));
        data.Add(new SomeData("Sean", "Sunset Blvd"));
        List<SomeData> result = data.Where(BuildExpression("Name", "Mark")).ToList();
        List<SomeData> result2 = data.Where(BuildExpression("Address", "Wilmington Road")).ToList();
    }
    private Func<SomeData, bool> BuildExpression(string propertyName, string value)
    {
        ParameterExpression pe = Expression.Parameter(typeof(SomeData), "someData");
        Expression left = Expression.Property(pe, propertyName);
        Expression right = Expression.Constant(value);
        BinaryExpression binary = Expression.Equal(left, right);
        Expression<Func<SomeData, bool>> lambda = Expression.Lambda<Func<SomeData, bool>>(binary, pe);
        return lambda.Compile();
    }