|| (或)Linq中使用C#的操作员

Ev.*_*Ev. 12 .net c# linq null linq-to-sql

我正在使用linq过滤选择的MessageItems.我编写的方法接受一堆可能为null的参数.如果它们为null,则应忽略该文件的条件.如果它不为null,则使用它来过滤结果.

这是我的理解,当做一个|| operation是C#,如果第一个表达式为true,则不应计算第二个表达式.

例如

if(ExpressionOne() || ExpressionTwo())
{
     // only ExpressionOne was evaluated because it was true
}
Run Code Online (Sandbox Code Playgroud)

现在,在linq,我正在尝试这个:

var messages = (from msg in dc.MessageItems
where  String.IsNullOrEmpty(fromname) || (!String.IsNullOrEmpty(fromname) && msg.FromName.ToLower().Contains(fromname.ToLower()))
select msg);
Run Code Online (Sandbox Code Playgroud)

我原本以为这会是合理的,因为它String.IsNullOrEmpty(fromname)等于是真的和||的第二部分 不会跑.

然而它确实运行了,第二部分

msg.FromName.ToLower().Contains(fromname.ToLower()))
Run Code Online (Sandbox Code Playgroud)

抛出一个空引用异常(因为fromname为null)!! - 我得到一个经典的"对象引用没有设置为对象的实例"异常.

有帮助吗?

Shu*_*oUk 14

阅读本文档,了解linq和c#如何实现断开连接.

由于Linq表达式应该简化为普通方法以外的其他方法,因此如果稍后在某些非Linq to Objects上下文中使用它,则可能会发现此代码中断.

那就是说

String.IsNullOrEmpty(fromname) || 
(   !String.IsNullOrEmpty(fromname) && 
    msg.FromName.ToLower().Contains(fromname.ToLower())
)
Run Code Online (Sandbox Code Playgroud)

因为它应该真的很糟糕

String.IsNullOrEmpty(fromname) || 
msg.FromName.ToLower().Contains(fromname.ToLower())
Run Code Online (Sandbox Code Playgroud)

这使得很好,很清楚你依赖于msg和msg.FromName也都是非空的.

为了让您的生活更轻松,您可以添加以下字符串扩展方法

public static class ExtensionMethods
{
    public static bool Contains(
        this string self, string value, StringComparison comparison)
    {
        return self.IndexOf(value, comparison) >= 0;
    }

    public static bool ContainsOrNull(
        this string self, string value, StringComparison comparison)
    {
        if (value == null)
            return false;
        return self.IndexOf(value, comparison) >= 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用:

var messages = (from msg in dc.MessageItems
where  msg.FromName.ContainsOrNull(
    fromname, StringComparison.InvariantCultureIgnoreCase)
select msg);
Run Code Online (Sandbox Code Playgroud)

然而,这不是问题.问题是系统的Linq to SQL方面试图使用该fromname值来构造发送到服务器的查询.

因为fromname是一个变量,所以转换机制会消失,并且会对它做出什么要求(fromname即使它是null,也会产生小写表示,从而触发异常).

在这种情况下,您可以执行您已经发现的操作:保持查询不变,但确保始终可以创建具有所需行为的非null fromname值,即使它为null.

也许更好的是:

IEnumerable<MessageItem> results;
if (string.IsNullOrEmpty(fromname))
{ 
    results = from msg in dc.MessageItems 
    select msg;    
}
else
{
    results = from msg in dc.MessageItems 
    where msg.FromName.ToLower().Contains(fromname) 
    select msg;    
}
Run Code Online (Sandbox Code Playgroud)

这不是很好,它的查询包含其他约束,因此调用了更多的重复,但对于简单查询实际上应该导致更易读/可维护的代码.如果您依赖匿名类型,这会很痛苦,但希望这对您来说不是问题.

  • 因为像sql引擎这样的东西不能保证做那种短路评估(不需要诉诸像案例这样糟糕的事情),Linq系统不保证会发生这种情况(因此关于它的文档).您不必编写扩展名 - 它已经在答案中了. (2认同)

Ev.*_*Ev. 5

好的.我找到解决方案.

我将违规行更改为:

where (String.IsNullOrEmpty(fromemail)  || (msg.FromEmail.ToLower().Contains((fromemail ?? String.Empty).ToLower())))
Run Code Online (Sandbox Code Playgroud)

它有效,但感觉就像一个黑客.我确定如果第一个表达式为真,则第二个表达式不应该被评估.

如果有人能为我确认或否认这一点会很棒......

或者如果有人有更好的解决方案,请告诉我!