Linq查询中的异常处理

时间:2013-10-19 05:35:06

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

我正在使用......

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

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

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

4 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:4)

这是您可以做的最简单的更改,只需排除null或引发异常的项目。

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

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

答案 2 :(得分:3)

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

Linq允许您定义一个表达式树,该表达式树稍后由可能是或不是C#的东西执行(例如,表达式可以转换为SQL查询)。如果你正在编写linq,你的查询提供程序(表达式转换的东西)很可能不支持异常处理(更不用说你正在做什么抛出异常)。

另一方面,

对象(或“linq to objects”)最终只能在C#中执行,因此您可以对异常处理进行粗略的处理。

例如w / linq到对象,你可以这样做:

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#code),这意味着它将在.Net进程中运行,即使我们用linq替换myList到实体表(或其他一些linq提供程序)。原因是C#表达式语法不支持块,也不支持try-catch。不出所料,鉴于此,SQL样式的Linq语句(来自xxx select yyy)也不支持try-catch块。

但是,仅仅因为C#表达式语法不支持它并不意味着你不能这样做。但是,要清楚,我不建议这样做,因为我高度怀疑是否存在支持它的QueryProvider(除了linq到对象提供者)。好奇的是,你将如何创建一个包含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时,您可以使用它。

答案 3 :(得分:2)

这取决于你想要达到的目的:

  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块中