简体   繁体   English

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

[英]LINQ: adding where clause only when a value is not null

I know a typical way is like this:我知道一个典型的方式是这样的:

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

However, from a program we took over from other developers, we saw code like this:但是,从我们从其他开发人员那里接手的程序中,我们看到了这样的代码:

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

If this is a normal SQL statement, I would definitely say that the 2nd one is a bad practice.如果这是一条普通的 SQL 语句,我肯定会说第二条是不好的做法。 Because it adds a meaningless where clause to the query when name1 is null.因为它在 name1 为空时向查询添加了一个无意义的 where 子句。

But I am new to LINQ, so I am not sure if LINQ is different?但我是 LINQ 的新手,所以我不确定 LINQ 是否不同?

you can write it like你可以这样写

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

This way second part of your condition will not be evaluated if your first condition evaluates to false这样,如果您的第一个条件评估为 false,则不会评估您的条件的第二部分

Update:更新:
if you write如果你写

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

and name1 is null second part of your condition will not be evaluated since or condition only requires one condition to return true并且 name1 为 null 您的条件的第二部分将不会被评估,因为 or 条件只需要一个条件才能返回 true

plz see this link for further detail请参阅此链接以获取更多详细信息

Often this sort of thing feels smoother to write using the fluent syntax, rather than the query syntax.通常,使用流利的语法而不是查询语法来编写这种东西感觉更流畅。

eg例如

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

So if name1 is null, you just don't do any Where() call.因此,如果name1为空,您就无需执行任何Where()调用。 If you have multiple different filters, all of which may or may not be required, and perhaps various different sort orders, I find this becomes a lot more manageable.如果您有多个不同的过滤器,所有这些可能需要也可能不需要,并且可能有各种不同的排序顺序,我发现这变得更易于管理。

Edit for alex: OK, I was answering the question about adding a where clause only when a value is not null.为 alex 编辑:好的,我正在回答有关仅在值不为空时添加 where 子句的问题。 In response to the other part of the question, I tried this out with Entity Framework 4 to see what SQL that LINQ produced.为了回答问题的另一部分,我使用 Entity Framework 4 进行了尝试,以查看 LINQ 生成的 SQL。 You do this by casting query to an ObjectQuery and calling .ToTraceString() .为此,您可以将query转换为ObjectQuery并调用.ToTraceString() The results were that the WHERE clause came out as follows:结果是WHERE子句出来如下:

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

So, yes, it's classic bad SQL, if you have an index on the name column, don't expect it to be used.所以,是的,这是经典的坏 SQL,如果你在name列上有索引,不要指望它会被使用。

Edit #2: Tried this again using LINQ to SQL rather than Entity Framework, with rather different results.编辑 #2:使用 LINQ to SQL 而不是 Entity Framework 再次尝试,结果完全不同。 This time, trying the query with name1 being null results in no WHERE clause at all, as you'd hope;这一次,尝试name1为 null 的查询根本不会产生WHERE子句,正如您所希望的那样; trying it with name1 being "a" resulted in a simple WHERE [t0].[name] = @p0 and @p0 sent as "a".尝试将name1 “a”会导致一个简单的WHERE [t0].[name] = @p0@p0作为“a”发送。 Entity Framework does not seem to optimize thus.实体框架似乎没有因此优化。 That's a bit worrying.这有点令人担忧。

The best way to do this is to create yourself an extension method that will take in a conditional statement and a where expression.最好的方法是为自己创建一个扩展方法,该方法将接受条件语句和 where 表达式。 If the condition is true then it will use the where expression else it will not use it.如果条件为真,那么它将使用 where 表达式,否则将不使用它。 This can dramatically clean up your code, eliminating the need for if statements.这可以显着清理您的代码,消除对 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;
    }
}

Now you can write your code like this:现在您可以像这样编写代码:

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

So I tried the .Where(..., x => ...) extension method listed here as an answer but it doesn't work against Entity Framework as Linq To Entities doesn't know how to translate that into TSQL.因此,我尝试了此处列出的.Where(..., x => ...)扩展方法作为答案,但它不适用于实体框架,因为 Linq To Entities 不知道如何将其转换为 TSQL。

So here's my solution getting my Func on:所以这是我的解决方案让我的 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 in my case is a string parameter on the encapsulating method with a default value of NULL.在我的情况下, someColumnBeingFilteredValue是封装方法上的字符串参数,默认值为 NULL。

I like use the Expression eg我喜欢使用表达式,例如

    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);

For EF Core I broke it up like this:对于 EF Core,我将其分解为:

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)
{
}

I had to be explicit with my typing instead of relying on var .我必须明确输入而不是依赖var

LINQ is diffrent in some other causes (not in this causes), LINQ is the way to get data in the "Faster way" with a littel code and clear cod as possible, there a many benefits of LINQ: LINQ 在其他一些原因上有所不同(不是在这个原因中),LINQ 是一种以“更快的方式”获取数据的方式,使用尽可能少的代码和清晰的代码,LINQ 有很多好处:

  1. Makes it easier to transform data into objects.更容易将数据转换为对象。 I'm sure you've heard the term "Impedence Mismatch" being used quite often, meaning that LINQ reduces the amount of work you must do to translate between object-oriented code and data paradigms such as hierarchical, flat-file, messages, relational, and more.我相信您已经听说过经常使用“阻抗不匹配”这个术语,这意味着 LINQ 减少了在面向对象代码和数据范例(例如分层、平面文件、消息、关系,等等。 It doesn't eliminate the "Impedence Mismatch" because you must still reason about your data in its native form, but the bridge from here to there is (IMO) much shorter.它并没有消除“阻抗不匹配”,因为您仍然必须以原始形式对数据进行推理,但是从这里到那里的桥梁(IMO)要短得多。

  2. A common syntax for all data.所有数据的通用语法。 Once you learn query syntax, you can use it with any LINQ provider.一旦学习了查询语法,就可以将它与任何 LINQ 提供程序一起使用。 I think this is a much better development paradigm than the Tower of Babel that has grown over the years with data access technologies.我认为这是一个比多年来随着数据访问技术而发展起来的巴别塔更好的开发范式。 Of course, each LINQ provider has unique nuances that are necessary, but the basic approach and query syntax is the same.当然,每个 LINQ 提供程序都有必要的独特细微差别,但基本方法和查询语法是相同的。

  3. Strongly typed code.强类型代码。 The C# (or VB.NET) query syntax is part of the language and you code with C# types, which are translated into something a provider understands. C#(或 VB.NET)查询语法是语言的一部分,您使用 C# 类型进行编码,这些类型被翻译成提供者可以理解的内容。 This means that you gain the productivity of having your compiler find errors earlier in the development lifecycle than elsewhere.这意味着您可以让编译器在开发生命周期中比其他地方更早地发现错误,从而提高工作效率。 Granted, many errors in stored proc syntax will generate errors when you save, but LINQ is more general than SQL Server.当然,存储过程语法中的许多错误会在您保存时产生错误,但 LINQ 比 SQL Server 更通用。 You have to think of all the other types of data sources that generate runtime errors because their queries are formed with strings or some other loosely typed mechanism.您必须考虑生成运行时错误的所有其他类型的数据源,因为它们的查询是由字符串或其他一些松散类型的机制形成的。

  4. Provider integration.提供商集成。 Pulling together data sources is very easy.将数据源整合在一起非常容易。 For example, you can use LINQ to Objects, LINQ to SQL, and LINQ to XML together for some very sophisticated scenarios.例如,对于一些非常复杂的场景,您可以一起使用 LINQ to Objects、LINQ to SQL 和 LINQ to XML。 I think it's very elegant.我认为它非常优雅。

  5. Reduction in work.减少工作。 Before LINQ, I spent a lot of time building DALs, but now my DataContext is the DAL.在 LINQ 之前,我花了很多时间构建 DAL,但现在我的 DataContext 是 DAL。 I've used OPFs too, but now I have LINQ that ships with multiple providers in the box and many other 3rd party providers, giving me the benefits from my previous points.我也使用过 OPF,但现在我有 LINQ,它附带多个提供程序和许多其他 3rd 方提供程序,让我从之前的观点中受益。 I can set up a LINQ to SQL DataContext in a minute (as fast as my computer and IDE can keep up).我可以在一分钟内设置一个 LINQ to SQL DataContext(我的计算机和 IDE 可以跟上的速度)。

  6. Performance in the general case doesn't become an issue.一般情况下的性能不会成为问题。 SQL Server optimizes queries quite well these days, just like stored procs. SQL Server 现在可以很好地优化查询,就像存储过程一样。 Of course, there are still cases where stored procs are necessary for performance reasons.当然,仍然存在出于性能原因需要存储过程的情况。 For example, I've found it smarter to use a stored proc when I had multiple interactions between tables with additional logic inside of a transaction.例如,当我在表之间进行多次交互时,我发现使用存储过程会更聪明,并且事务内部有额外的逻辑。 The communications overhead of trying to do the same task in code, in addition to getting the DTC involved in a distributed transaction made the choice for a stored proc more compelling.除了让 DTC 参与分布式事务之外,尝试在代码中执行相同任务的通信开销使存储过程的选择更具吸引力。 However, for a query that executes in a single statement, LINQ is my preferred choice because even if there was a small performance gain from a stored proc, the benefits in previous points (IMO) carry more weight.但是,对于在单个语句中执行的查询,LINQ 是我的首选,因为即使存储过程的性能提升很小,前面几点 (IMO) 的好处也更重要。

  7. Built-in security.内置安全性。 One reason I preferred stored procs before LINQ was that they forced the use of parameters, helping to reduce SQL injection attacks.在 LINQ 之前我更喜欢存储过程的一个原因是它们强制使用参数,有助于减少 SQL 注入攻击。 LINQ to SQL already parameterizes input, which is just as secure. LINQ to SQL 已经参数化输入,这同样安全。

  8. LINQ is declarative. LINQ 是声明性的。 A lot of attention is paid to working with LINQ to XML or LINQ to SQL, but LINQ to Objects is incredibly powerful.使用 LINQ to XML 或 LINQ to SQL 非常关注,但 LINQ to Objects 非常强大。 A typical example of LINQ to Objects is reading items from a string[]. LINQ to Objects 的一个典型示例是从 string[] 中读取项目。 However, that's just a small example.然而,这只是一个小例子。 If you think about all of the IEnumerable collections (you can also query IEnumerable) that you work with every day, the opportunities are plentiful.如果您考虑您每天使用的所有 IEnumerable 集合(您也可以查询 IEnumerable),那么机会很多。 ie Searching an ASP.NET ListBox control for selected items, performing set operations (such as Union) on two collections, or iterating through a List and running a lambda in a ForEach of each item.即在 ASP.NET ListBox 控件中搜索选定项,对两个集合执行集合操作(​​例如 Union),或者遍历 List 并在每个项的 ForEach 中运行 lambda。 Once you begin to think in LINQ, which is declarative in nature, you can find many of your tasks to be simpler and more intuitive than the imperative techniques you use today.一旦您开始使用本质上是声明性的 LINQ 进行思考,您会发现您的许多任务比您今天使用的命令式技术更简单、更直观。

I could probably go on, but I'd better stop there.我可能可以继续,但我最好停在那里。 Hopefully, this will provide a more positive view of how you could be more productive with LINQ and perhaps see it as a useful technology from a broader perspective.希望这将为您如何提高使用 LINQ 的工作效率提供更积极的观点,并可能从更广泛的角度将其视为一种有用的技术。

I've seen this pattern in standard SQL, and it seems useful if you have several parameters that may be NULL.我在标准 SQL 中看到过这种模式,如果您有几个可能为 NULL 的参数,它似乎很有用。 For example:例如:

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

If you see this in LINQ, it's possible they just blindly translated their old SQL-queries.如果您在 LINQ 中看到这一点,那么他们可能只是盲目地翻译了他们的旧 SQL 查询。

I like the idea with Extension我喜欢扩展的想法

public static IQueryable<T> WhereIf<T>(this IQueryable<T> query, bool condition, Expression<Func<T, bool>> whereClause)
        => condition ? query.Where(whereClause) : query;

No, I am not strongly agree with you.不,我不太同意你的观点。 here you just gave a simple logic在这里你只是给出了一个简单的逻辑

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

but what will happen if you do something different with the name1 that have null value..!!但是,如果您对具有空值的 name1 执行不同的操作会发生什么......!! Ok, now consider this situation.好的,现在考虑这种情况。 In this example you shows how to handle possible null values in source collections.在此示例中,您将展示如何处理源集合中可能的空值。 An object collection such as an IEnumerable<T> can contain elements whose value is null.IEnumerable<T>这样的对象集合可以包含值为 null 的元素。 If a source collection is null or contains an element whose value is null, and your query does not handle null values, a NullReferenceException will be thrown when you execute the query.如果源集合为 null 或包含值为 null 的元素,并且您的查询不处理 null 值,则执行查询时将引发NullReferenceException

Probably this could be a issue...可能这可能是一个问题......

I use the extension method below.我使用下面的扩展方法。 It's less flexible than the WhereIf extension from the other answers, but it's shorter to use.它不如其他答案中的 WhereIf 扩展灵活,但使用起来更短。

public static IQueryable<T1> FilterBy<T1, T2>(this IQueryable<T1> query, T2 expectedValue, Expression<Func<T1, T2>> propertyAccessor)
{
    if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor));
    if (expectedValue == null) return query;
    var equalExpr = Expression.Equal(propertyAccessor.Body, Expression.Constant(expectedValue, typeof(T2)));
    var lambda = Expression.Lambda<Func<T1, bool>>(equalExpr, propertyAccessor.Parameters);
    return query.Where(lambda);
}

It can be used like:它可以像这样使用:

var query = dataContext.Staffs.FilterBy(name, s => s.Name);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM