LINQ:仅当值不为null时才添加where子句

我知道一种典型的方式是这样的:

IQueryable query = from staff in dataContext.Staffs; if(name1 != null) { query = from staff in query where (staff.name == name1); } 

但是,从我们从其他开发人员手中接过的程序中,我们看到了这样的代码:

 IQueryable query = from staff in dataContext.Staffs; query = from staff in query where (name1 == null || staff.name == name1); 

如果这是一个普通的SQL语句,我肯定会说第二个是不好的做法。 因为当name1为null时,它会向查询添加无意义的where子句。

但我是LINQ的新手,所以我不确定LINQ是否有所不同?

你可以这样写

 IQueryable query = from staff in dataContext.Staffs; query = from staff in query where (name1 != null && staff.name == name1); 

这样,如果您的第一个条件评估为false,则不会评估条件的第二部分

更新:
如果你写

 IQueryable query = from staff in dataContext.Staffs; query = from staff in query where (name1 == null || staff.name == name1); 

并且name1为null您的条件的第二部分将不会被评估,因为或条件只需要一个条件返回true

请参阅此链接以获取更多详细信息

通常,使用流畅的语法而不是查询语法来编写这样的东西会更顺畅。

例如

 IQueryable query = dataContext.Staffs; if(name1 != null) { query = query.Where(x => x.name == name1); } 

因此,如果name1为null,则不执行任何Where()调用。 如果您有多个不同的filter,所有这些filter可能需要或可能不需要,也许各种不同的排序顺序,我发现这变得更易于管理。

编辑alex:好的,我正在回答有关仅在值不为null时添加where子句的问题。 在回答问题的其他部分时,我尝试使用Entity Framework 4来查看LINQ生成的SQL。 您可以通过将query转换为ObjectQuery并调用.ToTraceString() 。 结果是WHERE子句如下:

 WHERE @p__linq__0 IS NULL OR [Extent1].[name] = @p__linq__1 

所以,是的,它是经典的错误SQL,如果你在name列上有一个索引,不要指望它被使用。

编辑#2:使用LINQ to SQL而不是entity framework再次尝试这一点,结果相当不同。 这一次,尝试将name1设为null的查询根本不会产生任何WHERE子句,正如您所希望的那样; 尝试将name1作为“a”导致一个简单的WHERE [t0].[name] = @p0@p0发送为“a”。 entity framework似乎没有优化。 这有点令人担忧。

执行此操作的最佳方法是创建一个扩展方法 ,该方法将接受条件语句和where表达式。 如果条件为真,那么它将使用where表达式,否则它将不使用它。 这可以大大清理代码,无需if语句。

 public static class LinqExtensions { public static IQueryable WhereIf(this IQueryable query, bool condition, Expression> whereClause) { if (condition) { return query.Where(whereClause); } return query; } } 

现在您可以像这样编写代码:

 IQueryable query = dataContext.Staffs.AsQueryable().WhereIf(name1 != null, x => x.Name == name1); 

LINQ在其他一些原因中不同(不是因为这个原因),LINQ是以“更快的方式”获取数据的方法,尽可能使用littel代码和clear cod,LINQ有很多好处:

  1. 使数据转换为对象更容易。 我确信你已经听说过“Impedence Mismatch”一词经常被使用,这意味着LINQ减少了在面向对象的代码和数据范例(如分层,平面文件,消息)之间进行转换所必须做的工作量。关系等等。 它并没有消除“Impedence Mismatch”,因为你仍然必须以其原生forms推理你的数据,但从这里到那里的桥梁(IMO)要短得多。

  2. 所有数据的通用语法。 一旦学习了查询语法,就可以将它与任何LINQ提供程序一起使用。 我认为这是一个比巴塞尔塔更好的发展模式,多年来随着数据访问技术不断发展。 当然,每个LINQ提供程序都有必要的独特细微差别,但基本方法和查询语法是相同的。

  3. 强类型代码。 C#(或VB.NET)查询语法是语言的一部分,您使用C#类型进行编码,这些类型被转换为提供者理解的内容。 这意味着您可以提高编译器在开发生命周期的早期发现错误的效率。 当然,存储过程语法中的许多错误会在您保存时产生错误,但LINQ比SQL Server更通用。 您必须考虑生成运行时错误的所有其他类型的数据源,因为它们的查询是使用字符串或其他一些松散类型的机制形成的。

  4. 提供商集成。 将数据源整合在一起非常容易。 例如,您可以将LINQ to Objects,LINQ to SQL和LINQ to XML一起用于一些非常复杂的场景。 我觉得它非常优雅。

  5. 减少工作量。 在LINQ之前,我花了很多时间构建DAL,但现在我的DataContext是DAL。 我也使用过OPF,但是现在我的LINQ附带了多个提供商和许多其他第三方提供商,从我之前的观点中获益。 我可以在一分钟内设置LINQ to SQL DataContext(与我的计算机一样快,IDE可以跟上)。

  6. 一般情况下的表现不会成为问题。 SQL Server最近很好地优化了查询,就像存储过程一样。 当然,由于性能原因,仍然存在需要存储过程的情况。 例如,当我在表之间进行多次交互并且在事务内部具有额外逻辑时,我发现使用存储过程更聪明。 尝试在代码中执行相同任务的通信开销,除了使DTC涉及分布式事务之外,还使得存储过程的选择更具吸引力。 但是,对于在单个语句中执行的查询,LINQ是我的首选,因为即使存储过程中的性能提升很小,以前的优点(IMO)也会带来更大的好处。

  7. 内置安全性。 在LINQ之前我首选存储过程的一个原因是它们强制使用参数,有助于减少SQL注入攻击。 LINQ to SQL已经参数化输入,这同样安全。

  8. LINQ是声明性的。 使用LINQ to XML或LINQ to SQL非常注意,但LINQ to Objects非常强大。 LINQ to Objects的典型示例是从字符串[]中读取项目。 然而,这只是一个小例子。 如果您考虑每天使用的所有IEnumerable集合(您也可以查询IEnumerable),那么机会很多。 即搜索所选项目的ASP.NET ListBox控件,对两个集合执行集合操作(​​如Union),或迭代List并在每个项目的ForEach中运行lambda。 一旦开始考虑LINQ(本质上是声明性的),您可以发现许多任务比您当前使用的命令式技术更简单,更直观。

我可能会继续,但我最好停在那里。 希望这将为您提供更积极的观点,让您了解如何通过LINQ提高工作效率,并从更广阔的视角将其视为有用的技术。

我在标准SQL中看到过这种模式,如果你有几个可能为NULL的参数,它似乎很有用。 例如:

 SELECT * FROM People WHERE ( @FirstName IS NULL OR FirstName = @FirstName ) AND ( @LastName IS NULL OR LastName = @LastName ) 

如果你在LINQ中看到这一点,他们可能只是盲目地翻译他们的旧SQL查询。

我喜欢使用表达式,例如

  Expression> expresionFinal = c => c.Active == true; if (DateBirth.HasValue) { Expression> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth); expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate); } IQueryable query = dataContext.Persons; query = query.Where(expresionFinal); 

所以我尝试了这里列出的.Where(..., x => ...)扩展方法作为答案,但它不能对Entity Framework起作用,因为Linq To Entities不知道如何将其转换为TSQL。

所以这是我的解决方案让我的Func:

 Expression> columnBeingFilteredPredicate = x => true; // Default expression to just say yes if (!string.IsNullOrWhiteSpace(someColumnBeingFilteredValue)) { columnBeingFilteredPredicate = x => x.someColumnBeingFiltered == someColumnBeingFilteredValue; } _context.SomeEfPocos.Where(x => ..... && ..... && ..... &&) .Where(columnBeingFilteredPredicate); 

在我的情况下, someColumnBeingFilteredValue是封装方法的字符串参数,默认值为NULL。

不,我不是非常同意你的观点。 在这里你只是给了一个简单的逻辑

 if(name1 != null) // do your stuff 

但如果你做了与name1有不同价值的东西会发生什么…… !! 好的,现在考虑这种情况。 在此示例中,您将展示如何处理源集合中可能的空值。 诸如IEnumerable类的对象集合可以包含值为null的元素。 如果源集合为null或包含值为null的元素,并且查询NullReferenceException空值,则在执行查询时将抛出NullReferenceException

可能这可能是个问题……

对于EF Core,我将其分解为:

 IQueryable recs = contextApi.Partners; if (status != -1) { recs = recs.Where(i => i.Status == status); } recs = recs.OrderBy(i => i.Status).ThenBy(i => i.CompanyName); foreach (var rec in recs) { } 

我必须明确我的打字而不是依赖于var