【问题标题】:How to handle NULL object property with FirstOrDefault using Linq如何使用 Linq 使用 FirstOrDefault 处理 NULL 对象属性
【发布时间】:2016-07-21 05:35:34
【问题描述】:

我的实际应用程序问题如下所示

Employee empl = new Employee(397947, "David", "Redson", 80000);
        employees.Add(empl);
        employees.Add(new Employee(174966, "Alfred", "Swanson", 50000));
        employees.Add(new Employee(848024, "Alima", "Bieyrou", 40000));
        employees.Add(new Employee(number: 397462, fName: "Robert",
                                     lName: "Nants", salary: 30000));


string s = employees.Where(a => a.EmployeeNumber == 20000).FirstOrDefault().FirstName;

当我使用FirstOrDefault 时,当没有匹配的记录时会抛出错误。如果有匹配的记录,我想返回值,否则可以为null或为空..

【问题讨论】:

  • 为什么不在访问 FirstName 属性之前检查返回值是否不为空?
  • 你想要 SingleOrDefault()。
  • 我认为FirstOrDefault 是最佳选择,请参阅@hariprasad 的回答

标签: c# entity-framework linq


【解决方案1】:

在这种情况下,您不需要使用WhereFirstOrDefault,您可以在FirstOrDefault 本身内部指定过滤条件。但是如果没有满足条件的记录,这会给你 null (因为在没有第一个值的情况下,它会给你默认值,对于引用类型对象,默认值是null),你应该检查@987654326 @ 在访问值之前,将抛出 NullReferenceException。所以像这样使用:

var Employee=employees.FirstOrDefault(a => a.EmployeeNumber == 20000);
if(Employee!=null)
{
  string employee_name=Employee.FirstName;
  // code here
}

否则您可以使用?. 来检查null,如下所示:

string employee_name = employees.FirstOrDefault(a => a.EmployeeNumber == 20000)?.FirstName;

【讨论】:

  • 这种方法效率不高。 FirstOrDefault 将完整的 Employee 检索到本地,然后您丢弃大多数字段。使用您的 SQL 服务器分析器检查 SQL 语句。
  • 在可查询(LINQ-to-SQL 或 LINQ-to-Entities)集合中不允许使用空传播运算符 ?.(又名 Elvis 运算符)。但是,它适用于 LINQ-to-Objects 集合。
【解决方案2】:

在您的 FirstOrDefault 之前选择您的 linq 语句中的字符串,然后您将获得您的字符串或默认字符串:

string s = employees.Where(a => a.EmployeeNumber == 2000)
                    .Select(a => a.FirstName)
                    .FirstOrDefault();

这样做的好处是只获取您将使用的值,而不是完整的 Employee。

【讨论】:

  • 嗯,我想知道为什么这个最自然的解决方案被完全忽略了。 +1
  • 将 FirstOrDefault 放在最后通常意味着在选择第一个元素之前将整个结果加载到内存中。将 Where 替换为 FirstOrDefault 将导致底层查询将选择实现为 Top 1。这归结为底层 LINQ 提供程序的实现存在差异。
  • 我不同意。 FirstOrDefault 不会加载整个集合。根据参考资料。它获取枚举器,执行 ONE MoveNext 并返回 Current。见github.com/Microsoft/referencesource/blob/master/System.Core/…
  • 对于 IQueryable,它取决于持有 Queryable.Expression 的类。实体框架 DbSet 类使其成为 SQL“选择前 1 ...”所以对于 IQueryable 我也没有看到任何证据表明已下载完整集合。
  • 这应该是公认的答案,在大多数情况下更清晰、更短、传输的数据更少。
【解决方案3】:

也许您可以尝试使用空传播来使其更容易:

string s = employees.Where(a => a.EmployeeNumber == 20000).FirstOrDefault()?.FirstName;

【讨论】:

  • 值得一提的是,空传播运算符仅适用于 C# 6+
  • @fubo C#6 是 C# 的 current 版本,我不明白为什么我们需要提及这一点。对于其他 C# 版本,我们不会这样做。
  • 正如我之前评论的,这在可查询(LINQ-to-SQL 或 LINQ-to-Entities)集合中是不允许的,因为未翻译运算符。 @Ivan Stoev我们确实在其他答案中提到了C#版本,其中早期版本中未实现行为。通常会提到只有某些版本支持正在讨论的操作。
【解决方案4】:

您可以使用DefaultIfEmpty。考虑以下示例:

var entries = new Employee[0];
var result = entries.DefaultIfEmpty(new Employee() { FirstName = "<default name>" }).First().FirstName;

【讨论】:

    【解决方案5】:

    如果您确定给定的 EmployeeNumber 只有一条记录,您可以使用 SingleOrDefault 扩展名。

    var item = employees.SingleOrDefault(a => a.EmployeeNumber == 20000);
    string s = "";
    
    if(item!= null)
    {
        s = item.FirstName;
        // your logic ... 
    }
    

    如果给定的员工编号有多个记录,请使用FirstOrDefault,但在访问属性之前进行空检查。

    var item = employees.FirstOrDefault(a => a.EmployeeNumber == 20000);
    
    string s = "";    
    if(item!= null)
    {
        s = item.FirstName;
        // your logic ... 
    }
    

    【讨论】:

    • 他应该如何在你的 if 块之外使用变量 s?
    • 有可能出现多条记录,可以如下方式使用 if (employees.Where(a => a.EmployeeNumber == 397947).FirstOrDefault() != null) { string s = employees.Where(a => a.EmployeeNumber == 397947).FirstOrDefault().FirstName; }
    • 在这种情况下使用第二种方法。
    • @fubo 更新了它。谢谢。
    【解决方案6】:

    你可以像下面那样做

    var employee = employees.FirstOrDefault(a => a.EmployeeNumber == 20000);
    return employee != null ? employee.Name : string.Empty;
    

    【讨论】:

      【解决方案7】:

      C# 8 及以后使用null-coalescing 运算符?? 和空值检查运算符?

      像这样:

      string s = employees?.Where(a => a.EmployeeNumber == 20000)
                           .FirstOrDefault()?
                           .FirstName ?? string.Empty;
      

      为了避免employees 列表和任何员工属性中出现任何空异常。

      【讨论】:

        【解决方案8】:

        我认为最简单的方法就是写下一行:

        string firstName = employees?.FirstOrDefault(a => a.EmployeeNumber == 20_000)?.FirstName ?? null;
        

        代码的作用是:如果employees 不是null,并且如果它在employees 列表中找到一个对象,其中EmployeeNumber 等于20,000,并且如果此实例不为空,则' 将获得FisrtName 属性,否则字符串fisrtName 将为空。

        【讨论】:

          【解决方案9】:

          检查对象是否为空后赋值。

          var emp = employees.Where(a => a.EmployeeNumber == 20000).FirstOrDefault();
          
          string s = emp == null ? string.Empty: emp.FirstName;
          

          【讨论】:

          • Where有什么需要,可以在FirstOrDefault里面指定条件
          • 代替 var 可以像下面这样使用 if (employees.Where(a => a.EmployeeNumber == 397947).FirstOrDefault() != null) { string s = employees.Where(a = > a.EmployeeNumber == 397947).FirstOrDefault().FirstName; }
          • @python :为什么你会为了检查空条件而执行两次相同的表达式?最好将结果存储在变量中,然后检查变量的值。 DRY 原则——不要重复自己。
          【解决方案10】:
          string employee_name = employees.FirstOrDefault(a => a.EmployeeNumber == 20000)??new Employee();
          

          我们可以使用它并避免在未创建对象时发生空异常。

          【讨论】:

            【解决方案11】:

            EFCore6+/EF7+ 的注释,Queryable.FirstOrDefault Method 返回序列的第一个元素或默认值。

            NULL 不再返回。检查未找到值:

              int e = integerReturningQuery.FirstOrDefault();
              if (e == default) //default for int is 0
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-03-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多