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

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

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

private IQueryable 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; } 

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

您可以创建一个扩展方法

 public static IQueryable BoolWhere(this IQueryable source, Expression> 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) 

如果您有这些扩展方法:

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

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

 List> statements = new List>() { { (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; 

并只是将声明保存在一个地方。

动态LINQ

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

 var queries = new List>{ ((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。

这是有关PredicateBuilder的信息

这应该与LINQ to SQL兼容。

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

 private IQueryable GetSampleSearchQuery( SamplesCriteria criteria, Func>, Expression>, Expression>> 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 GetOrSampleSearchQuery( SamplesCriteria criteria) { return GetSampleSearchQuery(criteria, PredicateBuilder.Or); } private IQueryable GetAndSampleSearchQuery( SamplesCriteria criteria) { return GetSampleSearchQuery(criteria, PredicateBuilder.And); } 

您可以使用T4模板生成每个方法。 这样你仍然会有强类型的表达式。 右键单击您的项目,然后选择add-> New Item-> 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 <#= 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; } <#+ } #> 

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

您可以使用本答案中解释的技术。 因此可以调用Compose方法: – first.Compose(second,Expression.And); 或者first.Compose(第二,Expression.Or);

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

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