【问题标题】:Linq dynamic where count?Linq动态在哪里算?
【发布时间】:2012-12-02 14:05:56
【问题描述】:

是否可以在 where 中使用 list。我想要这样的想法:

public class Customer
{
    string FirtsName;
    string LastName;
    int Number;
    .....
}

我想使用复选框过滤客户。如果我选择 FirstName 和 Number 则将生成 where 子句

.where(x=> x.FirstName == "SomeFirstName" && x.Number == someNumber)

如果我只选择数字,那么将生成 where 子句

.where(x.Number == someNumber)

如果我选择 FirstName、LastName 和 Number 则将生成 where 子句

.where(x=> x.FirstName == "SomeFirstName" && x.Number == someNumber && x.LastName == "LastName")

我的意思不仅是动态列名,我也想生成 where 子句计数。列名和值来自列表:

我希望,我可以解释一下。提前致谢。

【问题讨论】:

    标签: c# linq where dynamic-linq


    【解决方案1】:

    使用不止一个步骤来生成查询:

    var query = originalList.AsQueryable();
    
    if(/*Filtering on First Name*/)
    {
      query = query.where(x => x.FirstName == FirstNameSearchString);
    }
    
    if(/*Filtering on Last Name */)
    {
      query = query.where(x => x.LastName == LastNameSearchString);
    }
    
    if(/*Filtering on Number*/)
    {
      query = query.where(x => x.Number== NumberSearchString);
    }
    
    //..And so on
    
    //Do stuff with query
    

    如果您不想执行多个 if 语句,我可以考虑其他两种方法:

    • 手动构建您自己的表达式树:

    Expression Trees (C# and Visual Basic) How to: Use Expression Trees to BUild Dynamic Queries (C# and Visual Basic)

    另一种方法是创建一个复杂的 where 子句,该子句使用 AND 和 OR 仅在选择此列搜索选项时进行过滤:

    .where(x => (IsFirstNameFilter == true && x.FirstName = FirstNameData) || (IsLastNameFilter == true && x.LastName = LastNameData) || ...);

    有了这个,你要小心执行时间,连接 SQL Profiler 看看发生了什么。

    我仍然推荐第一个选项。

    【讨论】:

    • 有没有动态的方法。我不想使用如果。因为要过滤的列多于 10 列,所以我应该使用 10 个 if 语句。
    【解决方案2】:

    你可以试试Dynamic LINQ。有了这个,你可以写这样的东西:

    var db = new NorthwindDataContext;
    var query = db.Products.Where("CategoryID=2 And UnitPrice>3").OrderBy("SupplierId");
    

    【讨论】:

    • 我扩展了查询字符串,不是吗?我可以动态创建 where 子句字符串吗?
    • 执行此操作时,您需要确保不会在代码中引入任何 SQL 注入机会。 SQL Injection - OWASP
    【解决方案3】:

    我认为上面的(第一个建议)答案很好 - 我在我当前的架构中有效地做同样的事情,但它包含更多,基本上分为两部分:

    1. 我有一个过滤器类(特定于任何相关实体......在你的情况下,可能被称为“CustomerFilter”) 这个类有一个看起来像这样的方法(只是一个例子)

      public class CustomerFilters
      {
          public IEnumerable<Expression<Func<Customer, bool>>> Filters()
          {
              if (/*check if should filter on FirstName here*/)
              {
                  yield return cust => cust.FirstName == FirstNameSearchString;
              }
              if (/*check if should filter on Surname here*/)
              {
                  yield return cust => cust.Surname == SurnameSearchString;
              }
          }
      }
      
    2. 我有一个扩展方法(作为助手,不一定必须是扩展方法),它本质上循环遍历所有过滤器并从中构建位置。代码看起来像这样(或多或少):

      public static IQueryable<T> Filter<T>(this IQueryable<T> collection, IEnumerable<Expression<Func<T, bool>>> filters)
      {
          var results = collection;
          foreach (var f in filters)
          {
              results = results.Where(f);
          }
          return results;
      }
      

    那么,它的用法可能是这样的:

    var query = db.Customers.Filter(myCustomerFilterClass.Filters());
    

    所以就像我说的那样,它与前面的答案相同(为了简洁起见,我在这里简化了很多代码),但我发现以这种方式将其包装起来并抽象为在有大量过滤器、大量实体并且要应用的特定过滤器是用户驱动的应用程序中非常有用。

    【讨论】:

      【解决方案4】:

      我会避免使用字符串,因为你会失去类型安全性。相反,我会使用表达式树来为您的 where 子句构建表达式。

              //The variable name for the parameter expression must match in all others
              var parameter = Expression.Parameter(typeof(Customer),"c");
      
              Expression<Func<Customer,bool>> firstNameCheck = c => c.FirstName == FirstNameSearchString;            
              Expression<Func<Customer,bool>> lastNameCheck = c => c.LastName == LastNameSearchString;
              Expression<Func<Customer,bool>> numberCheck = c => c.Number.ToString() == NumberSearchString;
      
              //Default to a true expression
              Expression ongoingExpression = Expression.Constant(true);
      
              if (//Filter on first name)
              {
                  ongoingExpression = Expression.AndAlso(ongoingExpression, Expression.Invoke(firstNameCheck, parameter));
              }
      
              if (//Filter on last name)
              {
                  ongoingExpression = Expression.AndAlso(ongoingExpression, Expression.Invoke(lastNameCheck, parameter));
              }
      
              if (//Filter on number)
              {
                  ongoingExpression = Expression.AndAlso(ongoingExpression, Expression.Invoke(numberCheck, parameter));
              }
      
      
      
              var lambda = Expression.Lambda<Func<Customer, bool>>(ongoingExpression, parameter);            
      
              query = query.Where(lambda.Compile());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-25
        • 1970-01-01
        • 1970-01-01
        • 2016-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多