通过 lambdas 构造 where 的表达式

Construct expression for where through lambdas

情况

我有一个接受 POCO 的方法。这个POCO长这样

    private class SearchCriteria
    {
      public string name get;set;
      public string age get;set;
      public string talent get;set;
    .. 
.... 
     }

该方法主要是对使用上述条件的数据库进行查询。

public void query(SearchCriteria crit)
{
  if(crit.name!=null && crit.age!=null && crit.talent !=null)
   {
     dbContext.Students.Where(c=>c.name ==crit.name &&    c.age==crit.age...)
   }
  else if(crit.name !=null && crit.age!=null)
  {
  }
  else if(....
  {
  }

正如您所看到的,上面有一个明确的问题,在大量条件的情况下,我将不得不编写大量的 if-elses 来从 where 子句中删除特定的参数。

可能的解决方案?

我实际上是 lambda 表达式世界的新手,但我相信我们一定有一个设施可以让我们做如下的事情。

dbContext.Students.Where(processCriteria(searchCriteriaPOCO)).

你们能指引我正确的方向吗?。谢谢

也许我遗漏了什么,因为代码示例不是我见过的最清晰的,但是对于您的具体示例,我认为以下内容应该没问题:

dbContext.Students.Where(c => (crit.name == null || crit.name == c.name) &&
    (crit.age == null || crit.age == c.age) &&
    (crit.talent == null || crit.talent == c.talent));

无需链接一堆 if 语句。

对于更复杂的场景,您可能更喜欢 PredicateBuilder

您可以使用这样的模式:

dbContext.Students.Where(c=>(crit.name == null || c.name ==crit.name) && ...)

为空的搜索条件将给出一个始终为真的子表达式。

获取可查询对象,然后不断向其添加 where 子句。这样您只需要测试一次每个可能的条件,并且只生成绝对需要的 where 子句的数量。

IQueryable<Student> q = dbContext.Students.AsQueryable();

if (crit.name != null)
    q = q.Where(c => c.name == crit.name);

if (crit.age != null)
    q = q.Where(c => c.age== crit.age);

首先让我说这个答案使用与@PhilWright 的 相同的基本思想。它只是将它包装在一个为您应用此模式的扩展方法中,并允许您拥有一个读起来不错的语法。

public static class SearchExtensions
{
    public static IQueryable<Student> Query(this SearchCriteria criteria, IQueryable<Student> studentQuery)
    {
        return studentQuery
            .Match(criteria.name, (student) => student.name == criteria.name)
            .Match(criteria.age, (student) => student.age == criteria.age)
            .Match(criteria.talent, (student) => student.talent == criteria.talent);
            // add expressions for other fields if needed.
    }

    private static IQueryable<Student> Match<T>(
        this IQueryable<Student> studentQuery,
        T criterionValue,
        Expression<Func<Student, bool>> whereClause) where T : class
    {
        // only use the expression if the criterion value is non-null.
        return criterionValue == null ? studentQuery : studentQuery.Where(whereClause);
    }
}

然后您可以像这样在您的代码中使用它:

var criteria = new SearchCriteria() { 
    name = "Alex", 
    talent = "Nosepicking" 
};
var results = criteria.Query(dbContext.Students);