LINQ to SQL - 如何有效地对多个条件进行AND或OR搜索

时间:2010-03-23 15:07:29

标签: c# asp.net-mvc linq linq-to-sql

我有一个ASP.NET MVC站点(使用Linq To Sql进行ORM)以及客户想要针对定制数据库的搜索工具,他们可以选择进行“AND”搜索(所有条件)匹配)或'或'搜索(任何标准匹配)。查询非常复杂且冗长,我想知道是否有一种简单的方法可以让它同时完成,而无需创建和维护两个不同版本的查询。

例如,当前的“AND”搜索看起来像这样(但这是很多简化版本):

private IQueryable<SampleListDto> GetSampleSearchQuery(SamplesCriteria criteria)
{
   var results = from r in Table where
            (r.Id == criteria.SampleId) &&
            (r.Status.SampleStatusId == criteria.SampleStatusId) &&
            (r.Job.JobNumber.StartsWith(criteria.JobNumber)) &&
            (r.Description.Contains(criteria.Description))
        select r;

}

我可以复制这个并替换&amp;&amp;与||运营商获得'OR'版本,但觉得必须有更好的方法来实现这一目标。有没有人有任何建议如何以一种易于维护的高效灵活的方式实现这一目标?感谢。

7 个答案:

答案 0 :(得分:2)

你可以按照

的方式创建一个扩展方法
public static IQueryable<T> BoolWhere<T>(this IQueryable<T> source, Expression<Func<T, TValue>> selector, bool isOr) {
  //use isOr value to determine what expression to build and add to the source
}

其中'isOr'将决定是否使用'和'表达式或'或'表达式。然后,您可以按照

的方式构建查询
bool isOr = true; //or false
var results = Data.BoolWhere(r => r.Id == criteria.SampleId, isOr)
  .BoolWhere(r => r.Status.SampleStatusId == criteria.SampleStatusId, isOr)
  .BoolWhere(r => r.Job.JobNumber.StartsWith(criteria.JobNumber), isOr)
  .BoolWhere(r => r.Description.Contains(criteria.Description), isOr)

答案 1 :(得分:2)

如果您有这些扩展方法:

public static class BoolExtensions
{
    public static bool And<TR, TC>(this IEnumerable<Func<TR, TC, bool>> statements, TR value, TC criteria)
    {
        foreach (var statement in statements)
        {
            if (!statement.Invoke(value, criteria))
            {
                return false;
            }
        }

        return true;
    }

    public static bool Or<TR, TC>(this IEnumerable<Func<TR, TC, bool>> statements, TR value, TC criteria)
    {
        foreach (var statement in statements)
        {
            if (statement.Invoke(value, criteria))
            {
                return true;
            }
        }

        return false;
    }
}

然后你可以将你的陈述声明为一个列表:

List<Func<TypeOfR, TypeOfC, bool>> statements = new List<Func<TypeOfR, TypeOfC, bool>>()
{
    { (r, c) => r.Id == c.SampleId },
    { (r, c) => r.Status.SampleStatusId == c.SampleStatusId },
    ...
};

并将您的查询编写为:

var results = from r in Table where
        statements.And(r, criteria)
    select r;

||版本:

var results = from r in Table where
        statements.Or(r, criteria)
    select r;

只需将陈述保存在一个地方。

答案 2 :(得分:1)

答案 3 :(得分:1)

或许比Jens的想法更容易想象,如果你只是寻找组合Or和组合And(而不是一些混合)你总是可以表达你的相等作为测试列表,然后应用Any或All运算符它。例如:

var queries = new List<Func<Table,SampleListDto,bool>>{
      ((a,b) => a.Id == b.SampleId),
      ((a,b) => a.Status.SampleStatusId == b.SampleStatusId),
      ((a,b) => a.Job.JobNumber.StartsWith(b.JobNumber)),
      ((a,b) => a.Description.Contains(b.Description))
};

var results = Table.Where(t=> queries.All(q => q(t, criteria)); // returns the && case
// or:  var results = Table.Where(t=>queries.Any(q=>q(t,criteria));

作为w / Jens',不知道这会如何有效地转化为SQL,但如果这成为一个问题,那么你最好还是将其转换为原生SQL。

答案 4 :(得分:1)

以下是有关的信息 PredicateBuilder

这应该与LINQ to SQL兼容。

可以创建一个新函数来使用PredicateBuilder函数And或Or:

private IQueryable<SampleListDto> GetSampleSearchQuery(
    SamplesCriteria criteria,
    Func<Expression<Func<SampleListDto, bool>>,
        Expression<Func<SampleListDto, bool>>,
        Expression<Func<SampleListDto, bool>>> logicExpr) 
{ 
   var results = from r in Table where 
            logicExpr(r => r.Id == criteria.SampleId,
            logicExpr(r => r.Status.SampleStatusId == criteria.SampleStatusId,
            logicExpr(r => r.Job.JobNumber.StartsWith(criteria.JobNumber),
            logicExpr(r => r.Description.Contains(criteria.Description)))))
        select r; 

}

And和Or函数看起来像:

private IQueryable<SampleListDto> GetOrSampleSearchQuery(
    SamplesCriteria criteria) 
{ 
    return GetSampleSearchQuery(criteria, PredicateBuilder.Or<SampleListDto>);
}
private IQueryable<SampleListDto> GetAndSampleSearchQuery(
    SamplesCriteria criteria)
{
    return GetSampleSearchQuery(criteria, PredicateBuilder.And<SampleListDto>);
}

答案 5 :(得分:1)

您可以使用T4模板生成每种方法。这样你仍然会有强类型的表达式。右键单击您的项目,然后选择add-&gt; New Item-&gt; Text Template

模板会像这样循环:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>

namespace YourNamespaceName
{
    public partial class YourClassName
    {
    <# Generate(true); #>
    <# Generate(false); #>
    }
}
<#+
    private void Generate(bool isOr)
    {
        string op = isOr ? "||" : "&&";
        string methodName = "GetSampleSearchQuery" + (isOr ? "Or" : "And");
#>
    private IQueryable<SampleListDto> <#= methodName #>(SamplesCriteria criteria)
    {
        var results = from r in Table where
                (r.Id == criteria.SampleId) <#= op #>
                (r.Status.SampleStatusId == criteria.SampleStatusId) <#= op #>
                (r.Job.JobNumber.StartsWith(criteria.JobNumber)) <#= op #>
                (r.Description.Contains(criteria.Description))
            select r;
        return results;
    }
<#+
    }
#>

只需将您的类更改为部分,并将任何其他所需的过滤器添加到T4模板,并使用<#= op #>而不是运算符。 (右键单击解决方案资源管理器中的T4模板,然后选择“运行自定义工具”以强制更新) 你最终会得到两种方法:GetSampleSearchQueryOrGetSampleSearchQueryAnd用于Or和an和过滤器重复,两者都只在一个地方维护。

答案 6 :(得分:1)

您可以使用this answer中说明的技巧。因此可以调用Compose方法: -     first.Compose(第二,Expression.And); 要么     first.Compose(第二,Expression.Or);

因此,操作类型只是一个参数。

通过在所有子句中重复应用它,您可以构建所需的表达式,并且它都是强类型和纯表达式代码。