|| (或)C# 中的 Linq 运算符
我正在使用 linq 来过滤选择的 MessageItems。 我编写的方法接受一堆可能为空的参数。 如果它们为空,则应忽略文件的条件。 如果不为空,则用它来过滤结果。
据我了解,当做 || 时 操作是 C# 的,如果第一个表达式为 true,则不应计算第二个表达式。
例如
if(ExpressionOne() || ExpressionTwo())
{
// only ExpressionOne was evaluated because it was true
}
,现在,在 linq 中,我正在尝试这样做:
var messages = (from msg in dc.MessageItems
where String.IsNullOrEmpty(fromname) || (!String.IsNullOrEmpty(fromname) && msg.FromName.ToLower().Contains(fromname.ToLower()))
select msg);
我本以为这会是合理的,因为 String.IsNullOrEmpty(fromname)
等于 true 并且 || 的第二部分 不会跑。
然而它确实运行了,第二部分
msg.FromName.ToLower().Contains(fromname.ToLower()))
抛出一个空引用异常(因为 fromname
为空)! - 我收到一个经典的“对象引用未设置到对象实例”异常。
有什么帮助吗?
I'm using linq to filter a selection of MessageItems. The method I've written accepts a bunch of parameters that might be null. If they are null, the criteria for the file should be ignored. If it is not null, use it to filter the results.
It's my understanding that when doing an || operation is C#, if the first expression is true, the second expression should not be evaluated.
e.g.
if(ExpressionOne() || ExpressionTwo())
{
// only ExpressionOne was evaluated because it was true
}
now, in linq, I'm trying this:
var messages = (from msg in dc.MessageItems
where String.IsNullOrEmpty(fromname) || (!String.IsNullOrEmpty(fromname) && msg.FromName.ToLower().Contains(fromname.ToLower()))
select msg);
I would have thought this would be sound, because String.IsNullOrEmpty(fromname)
would equal true and the second part of the || wouldn't get run.
However it does get run, and the second part
msg.FromName.ToLower().Contains(fromname.ToLower()))
throws a null reference exception (because fromname
is null)!! - I get a classic "Object reference not set to an instance of an object" exception.
Any help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
阅读此文档,其中解释了 linq 和 c# 如何体验断开。
由于 Linq 表达式预计会简化为普通方法以外的内容,因此如果稍后在某些非 Linq to Objects 上下文中使用此代码,您可能会发现该代码会中断。
这就是说,
它的形式很糟糕,因为它确实应该是这样
,这使得您依赖于 msg 和 msg.FromName 也都为非空,这很清楚。
为了让您的 C# 生活更轻松,您可以添加以下字符串扩展方法
然后使用:
但这不是问题。 问题在于系统的 Linq to SQL 方面正在尝试使用
fromname
值来构造发送到服务器的查询。由于
fromname
是一个变量,因此转换机制会关闭并执行所要求的操作(即使它为 null,也会生成fromname
的小写表示,这会触发异常) 。在这种情况下,您可以执行您已经发现的操作:保持查询不变,但确保始终可以创建具有所需行为的非空 fromname 值,即使它为空。
也许更好的是:
这并不是那么好,因为查询包含其他约束,因此涉及更多重复,但对于简单的查询实际上应该产生更多可读/可维护的代码。 如果您依赖匿名类型,这会很痛苦,但希望这对您来说不是问题。
Have a read of this documentation which explains how linq and c# can experience a disconnect.
Since Linq expressions are expected to be reduced to something other than plain methods you may find that this code breaks if later it is used in some non Linq to Objects context.
That said
Is badly formed since it should really be
which makes it nice and clear that you are relying on msg and msg.FromName to both be non null as well.
To make your life easier in c# you could add the following string extension method
Then use:
However this is not the problem. The problem is that the Linq to SQL aspects of the system are trying to use the
fromname
value to construct the query which is sent to the server.Since
fromname
is a variable the translation mechanism goes off and does what is asked of it (producing a lower case representation offromname
even if it is null, which triggers the exception).in this case you can either do what you have already discovered: keep the query as is but make sure you can always create a non null fromname value with the desired behaviour even if it is null.
Perhaps better would be:
This is not so great it the query contained other constraints and thus invovled more duplication but for the simple query actually should result in more readable/maintainable code. This is a pain if you are relying on anonymous types though but hopefully this is not an issue for you.
好的。 我找到了一个解决方案。
我将有问题的行更改为:
它有效,但感觉像是黑客。 我确信如果第一个表达式为真,则不应评估第二个表达式。
如果有人能为我确认或否认这一点,那就太好了...
或者如果有人有更好的解决方案,请告诉我!
Okay. I found A solution.
I changed the offending line to:
It works, but it feels like a hack. I'm sure if the first expression is true the second should not get evaluated.
Would be great if anyone could confirm or deny this for me...
Or if anyone has a better solution, please let me know!!!
如果您使用 LINQ to SQL,则您不能期望在 SQL Server 中出现相同的 C# 短路行为。 请参阅有关短路
WHERE
此问题 > SQL Server 中的子句(或缺少子句)。另外,正如我在评论中提到的,我不相信您会在 LINQ to SQL 中遇到此异常,因为:
String.IsNullOrEmpty(String)
不支持对 SQL 的转换,因此您可以'不要在 LINQ to SQL 中使用它。您确定这不是通过 LINQ to Objects 进行的吗? 您是否在源上调用 ToList() 或 ToArray() 或将其引用为 IEnumerable在运行此查询之前?
更新:阅读您的评论后,我再次测试了这一点并意识到了一些事情。 我对你不使用 LINQ to SQL 的看法是错误的。 您没有收到
“String.IsNullOrEmpty(String) 不支持对 SQL 的转换”
异常,因为IsNullOrEmpty()
正在局部变量上调用,而不是 SQL 列,因此它在客户端运行,即使您使用的是 LINQ to SQL(不是 LINQ to Objects)。 由于它在客户端运行,您可以在该方法调用上获得NullReferenceException
,因为它没有转换为SQL,在SQL中您无法获得NullReferenceException< /代码>。
让您的解决方案看起来不那么老套的一种方法是在查询之外解决
fromname
的“null-ness”:请注意,这并不总是被翻译成类似的内容(以您的评论为例):
它的翻译将在运行时根据
fromname
是否为 null 决定。 如果为 null,则将在不使用WHERE
子句的情况下进行翻译。 如果它不为 null,它将使用简单的“WHERE @theValue = theValue
”进行转换,而无需在 T-SQL 中进行 null 检查。因此最终,在这种情况下,SQL 中是否会短路的问题是无关紧要的,因为如果
fromname
是否为空,LINQ to SQL 运行时将发出不同的 T-SQL 查询。 从某种意义上说,它是在查询数据库之前在客户端短路的。If you are using LINQ to SQL, you cannot expect the same C# short-circuit behavior in SQL Server. See this question about short-circuit
WHERE
clauses (or lack thereof) in SQL Server.Also, as I mentioned in a comment, I don't believe you are getting this exception in LINQ to SQL because:
String.IsNullOrEmpty(String)
has no supported translation to SQL, so you can't use it in LINQ to SQL.Are you sure this is not going through LINQ to Objects somewhere? Are you calling ToList() or ToArray() on your source or referencing it as a IEnumerable<T> before running this query?
Update: After reading your comments I tested this again and realized some things. I was wrong about you not using LINQ to SQL. You were not getting the
"String.IsNullOrEmpty(String) has no supported translation to SQL"
exception becauseIsNullOrEmpty()
is being called on a local variable, not an SQL column, so it is running client-side, even though you are using LINQ to SQL (not LINQ to Objects). Since it is running client-side, you can get aNullReferenceException
on that method call, because it is not translated to SQL, where you cannot get aNullReferenceException
.One way to make your solution seem less hacky is be resolving
fromname
's "null-ness" outside the query:Note that this will not always be translated to something like (using your comments as example):
Its translation will be decided at runtime depending on whether
fromname
is null or not. If it is null, it will translate without aWHERE
clause. If it is not null, it will translate with a simple "WHERE @theValue = theValue
", without null check in T-SQL.So in the end, the question of whether it will short-circuit in SQL or not is irrelevant in this case because the LINQ to SQL runtime will emit different T-SQL queries if
fromname
is null or not. In a sense, it is short-circuited client-side before querying the database.您确定“fromname”为空,而不是“msg.FromName”为空吗?
Are you sure it's 'fromname' that's null and not 'msg.FromName' that's null?
就像布莱恩说的,我会在执行 ToLower().Contains(fromname.ToLower())) 之前查看 msg.FromName 是否为 null
Like Brian said, I would look if the msg.FromName is null before doing the ToLower().Contains(fromname.ToLower()))
您是正确的,因为您使用的是短路比较器,所以不应评估第二个条件(请参阅 有关 C# 短路评估的最佳实践是什么?),但是我怀疑 Linq 可能会在执行查询之前和执行此操作时尝试优化您的查询可能会改变执行顺序。
对我来说,将整个内容括在括号中也可以使声明更清晰,因为整个“where”条件都包含在括号内。
You are correct that the second conditional shouldn't get evaluated as you are using the short-circuit comparitors (see What is the best practice concerning C# short-circuit evaluation?), however I'd suspect that the Linq might try to optimise your query before executing it and in doing so might change the execution order.
Wrapping the whole thing in brackets also, for me, makes for a clearer statement as the whole 'where' condition is contained within the parenthases.