2011-04-26 130 views
17

我知道一个典型的方法是这样的:LINQ:添加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是不同的?

+0

您可以使用SQL Server Profiler查看查询归结到的内容。 – alex 2011-04-26 08:34:39

+1

在SQL中,它并不重要,因为“name1”在运行时是已知值,查询优化器将优化WHERE子句。事实上,您始终都会在SQL中看到这种风格。在LINQ中这样做的好处是代码更简洁。它可能不会更改由LINQ-to-SQL生成的SQL语句(它可能已经消除了它),并且它绝对不会影响SQL语句的性能,即使它停留在那里。 – 2011-04-26 11:17:08

+0

所以我一开始就错了...... 了解了,谢谢~~! – Henry 2011-04-28 07:46:56

回答

10

通常,使用流畅的语法而不是查询语法来写这种事情会更顺畅。

例如

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

所以,如果name1为空,你干脆不要做任何Where()电话。如果您有多个不同的过滤器,所有这些过滤器都可能需要也可能不需要,也可能有各种不同的排序顺序,我发现这变得更容易管理。

编辑为alex:好吧,我只是回答有关添加一个where子句只有当一个值不为null的问题。为了回答这个问题的其他部分,我使用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而不是实体框架,结果相当不同。这一次,试图将name1为空的查询根本不会导致WHERE条款,正如您所期望的那样;试图用name1为“a”导致简单的WHERE [t0].[name] = @p0@p0作为“a”发送。因此实体框架不是似乎优化。这有点令人担忧。

+3

这似乎并不能回答这个问题。 – alex 2011-04-26 08:33:42

+1

这不是很清楚问题是什么 – Vedran 2011-04-26 08:42:18

+0

我没有看到你的答案是如何相关的。 – 2011-04-26 08:48:35

2

LINQ是其他一些原因(在这导致), LINQ,就是以“更快的方式”数据与LITTEL代码和明确的鳕鱼作为LINQ的可能,出现了许多好处的方式不同势:

  1. 使数据转换成对象更容易。我相信你已经听说过“Impedence Mismatch”这个词经常被使用,这意味着LINQ可以减少在面向对象代码和数据范例(如分层,平面文件,消息等)之间进行翻译所需的工作量。关系等等。它不会消除“Impedence Mismatch”,因为您仍然必须以原生形式推理您的数据,但从这里到那里的桥梁(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,并可能从更广泛的角度将其视为有用的技术。

15

,如果你的第一个条件计算为false

更新,你可以把它写像

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

这样你的病情的第二部分将不进行评估:
如果你写

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

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

PLZ看到这样link进一步详细

+1

如果'name1'为空,他希望返回所有'Staffs'记录:在这种情况下,您的重写将不会返回任何内容。 – Carson63000 2011-04-26 10:20:50

+0

@ Carson63000谢谢提及 – 2011-04-26 10:30:38

+0

为什么这个答案不是被否定的否定?当时,你认为你在这里回答什么问题?...问题是'name1',作为应用程序范围中的一个变量,是否正在被转换为sql并进行求值......它是哪个?'@ p__linq__0 IS NULL'检查; '@ p_linq_0'这里是作为参数传递的变量。 – 2016-11-24 05:33:07

0

不,我不是强烈同意你的看法。 在这里你只给一个简单的逻辑

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

,但如果你做的东西与拥有空值的名称1不同会发生什么.. !!好吧,现在考虑这种情况。 在本例中,您将展示如何处理源集合中可能的空值。 对象集合(如IEnumerable<T>)可以包含值为null的元素。 如果源集合为空或包含值为空的元素, 并且您的查询不处理空值,则在执行查询时将引发NullReferenceException

也许这可能是一个问题......

0

我已经看到了标准的SQL这种模式,如果你有几个参数,这些参数可以为NULL,似乎是有用的。例如:

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

如果你在LINQ中看到这个,可能他们只是盲目地翻译了他们以前的SQL查询。

+0

该模式的问题在于它不适合查询优化器。例如,如果“FirstName”或“LastName”上有索引,则不会使用它,因为根据传入的参数,“WHERE”子句可能涉及或不涉及表中的那些列。 – Carson63000 2011-04-26 10:22:26

1

我喜欢用表达式

Expression<Func<Persons, bool>> expresionFinal = c => c.Active == true; 

    if (DateBirth.HasValue) 
       { 
        Expression<Func<Persons, bool>> expresionDate = c => (EntityFunctions.TruncateTime(c.DateBirth) == DateBirth); 
        expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate); 
       } 

IQueryable query = dataContext.Persons; 
query = query.Where(expresionFinal); 
+0

差不多一个答案....应该解释你为什么不做'expresionDate = c =>(EntityFunctions.TruncateTime(c.DateBirth)== DateBirth)|| DateTime.HasValue'这里.. – 2016-11-24 05:39:04

+0

我不会建议,它会影响查询性能。如果用户没有通知日期不过滤 if(DateBirth.HasValue)< - 是一个搜索参数 – J4ime 2016-11-25 09:13:17

2

要做到这一点,最好的方法是创建自己的扩展方法,将采取在条件语句和地方表达。如果条件为真,那么它将使用where表达式,否则它将不使用它。这可以显着清理你的代码,消除if语句的需要。

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

现在你可以写你这样的代码:

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

这是一个非常好的主意,但我得到的错误:其他信息:LINQ to Entities无法识别方法Where。 ....并且此方法无法转换为商店表达式。 Sooo,EF无用我使用这个确切的方法没有错误。如果你给了我更多的信息,我可能会帮助你。确保你首先把你的DbSet,IEnumerable,ICollection或List变成IQueryable,否则C#将无法找到WhereIf扩展方法。 (编辑**我意识到我忘记了在我的例子中调用AsQueryable(),我添加了它,希望这有帮助,并且我更改了Where WhereIf的名称,并且更好地描述了该方法。) – 2016-11-23 09:51:04

+0

问题是使用Linq实体框架上的表达式使用后端Linq到实体映射器,并且每个使用的linq扩展方法只有在支持对TSQL(或使用任何一种DBMS的语言)进行翻译时才能使用。你的扩展方法(我也称为WhereIf)在任何内部类型上都可以正常工作,但是对于EF ...没有骰子。我把我的解决方案作为答案 – 2016-11-23 21:55:41

+0

当然,我遇到过几次不同的查询都遇到过这个问题。不过,我使用EF6,它有一个edmx文件,其中包含所有的实体模型。我在所有的查询中都使用了这个技巧,它可以将它转换为t-sql;我甚至可以打印出它生成的sql查询。 (我也只是用Linq to SQL来测试它,它工作)。我怀疑它很重要,但我正在使用Azure SQL,而不是SQL Server。无论如何,我希望你弄明白或找到适合你的解决方案。如果我可以帮助让我知道。 -Cheers – Soto 2016-11-23 22:48:54

0

所以,我想在这里列为答案.Where(..., x => ...)扩展方法,但它并没有对实体框架的工作为LINQ到实体没有按”不知道如何将其转换为TSQL。

因此,这里是我的解决方案让我的Func键上:

Expression<Func<SomeEfPoco, bool>> 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默认值的封装方法的字符串参数。

+0

这是不适用于这个问题或该扩展的表示形式..问题和扩展的前提是使用'someColumnBeingFilteredValue'与一个单独的谓词。这里的前提是** NOT **有一个本地变量条件检查的sql翻译;因此,在应用程序之外没有考虑这样的检查或评估。 – 2016-11-24 13:10:54

0

对于EF核心我打破了它像这样:

IQueryable<Partners> 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