【问题标题】:Exception handling in Linq queriesLinq 查询中的异常处理
【发布时间】:2013-10-19 05:35:05
【问题描述】:

我正在使用...

tmpLst = (from e in App.lstAllChilds where e.Id == Id select e).ToList();

其中lstAllChilds 是列表,其中还包含一些损坏的数据。

所以现在我正在处理这个查询中的 Try-Catch 块。
请帮忙。

【问题讨论】:

  • 这是 Linq to Entities 吗? Linq 到对象? lstAllChilds 的类型是什么?我无法理解您要准确捕获的异常...您真的得到了一个吗?
  • 它是我的自定义类列表... (List App.lstAllChilds)
  • 例外是 'InvalidObjectException' 和 'ArgumentNullException'
  • 我正在使用 Autodesk 的 Revit API 2014。
  • @Nitin 为什么不尝试在整个查询中使用try-catch?应该够了。

标签: c# linq exception-handling try-catch


【解决方案1】:

如果您只想忽略“坏元素”,那么:

App.lstAllChilds.SkipExceptions().Where( e => e.Id == Id).ToList();

扩展方法:

 public static class Extensions
    {
        public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values)
        {
            using (var enumerator = values.GetEnumerator())
            {
                bool next = true;
                while (next)
                {
                    try
                    {
                        next = enumerator.MoveNext();
                    }
                    catch
                    {
                        continue;
                    }

                    if (next) yield return enumerator.Current;
                }
            }
        }
    }

【讨论】:

    【解决方案2】:

    这是您可以进行的最简单的更改,以简单地排除为空或引发异常的项目。

    tmpLst = App.lstAllChilds.Where(e =>
    {
        try
        {
            return e != null && e.Id == Id;
        }
        catch
        {
            return false;
        }
    }).ToList();
    

    但在我看来,您可能应该调查并解决根本问题。这似乎不是应该预期异常的情况。

    【讨论】:

      【解决方案3】:

      所以,事情就是这样。有语言集成查询 (Linq),然后是产生的枚举(又名迭代器)。

      Linq 允许您定义一个表达式树,稍后由可能是也可能不是 C# 的东西执行(例如,表达式可以被翻译成 SQL 查询)。如果您正在编写 linq,那么您的查询提供程序(进行表达式翻译的东西)很有可能不支持异常处理(更不用说您正在做的任何事情都会引发异常)。

      另一方面,Interators(或“linq to objects”)最终会在 C# 中执行,因此您可以尽情地处理异常。

      例如 w/ linq to objects 你可以这样做:

      var myList = new[] { "1", "2", "BARF", "3" };
      var sum = myList.Select(str => {
        try {
          return Int32.Parse(str);
        } catch {
          return 0;
        }
      }).Aggregate((x, y) => x + y);
      

      如果您确实在对对象进行 linq,并且您只想跳过源 IEnumerable 引发异常的元素,请查看 Vladimir Gondarev 的答案。

      但是要理解的重要一点是,我们刚刚传递给 Select 调用的匿名函数不是 Expression(未编译的表达式树),它是 Func(指向已编译的c# 代码),这意味着它将在 .Net 进程中运行,即使我们将 myList 替换为 linq to entity 表(或其他一些 linq 提供程序)。原因是 C# 表达式语法不支持块,也不支持 try-catch。不出所料,SQL 风格的 Linq 语句(from xxx select yyy)也不支持 try-catch 块。

      但是,仅仅因为 C# 表达式语法不支持它并不意味着您不能这样做。但是,需要明确的是,我不建议这样做,因为我非常怀疑是否存在支持它的 QueryProvider(除了 linq to objects 提供程序)。对于好奇的人,这里是如何创建一个包含 try-catch 块的 lambda 表达式。

      var parseMethod = typeof(Int32).GetMethod("Parse", new[] { typeof(String) });
      var param = Expression.Parameter(typeof(String));
      var selectExp =
          Expression.Lambda<Func<String, Int32>>(
              Expression.TryCatch(
                  Expression.Call(parseMethod, param),
                  Expression.Catch(typeof(Exception), Expression.Constant(0))
              ),
              param
          );
      var sum = myList.Select(selectExp).Aggregate((x, y) => x + y);
      

      因此,当有人实现由支持异常处理的商店支持的 QueryProvider 时,您可以使用它。

      【讨论】:

        【解决方案4】:

        这取决于你到底想达到什么目标:

        1. 返回除“损坏”项目之外的列表。

          • 您可以尝试使用Where 子句来检查并过滤掉它们:

            App.lstAllChilds.Where(x => IsValidNode(x, Id)).ToList();
            

            显然你需要实现IsValidNode,它应该检查null,如果它可以抛出InvalidObjectException(不确定你是否可以轻松检测到它,但你总是可以将它包装在一个try-catch块中)和Id 平等。像这样:

            private bool IsValidNode(Child c, int id)
            {
                if (x == null) return false;
                try {
                    return c.Id == id;
                }
                catch (InvalidObjectException)
                {
            
                }
                return false;
            }
            
        2. 如果有任何损坏的项目,则返回一个空列表。

          • 只需将整个内容包装在一个 try-catch 块中

        【讨论】:

          猜你喜欢
          • 2013-05-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-29
          • 2021-04-23
          • 1970-01-01
          相关资源
          最近更新 更多