Exception handling in Linq queries

£可爱£侵袭症+ 提交于 2019-12-09 18:58:35

问题


I m using ...

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

where lstAllChilds is the list, which contains some corrupted data as well.

So now i m tying to handle Try-Catch block inside this query.
Please help.


回答1:


Here is the simplest change you can make to simply exclude items which are null or are raising an exception.

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

But in my opinion you probably should investigate and solve the underlying problem. This doesn't seem like a scenario where exceptions should be expected.




回答2:


In case if you just want to ignore "bad elements" then:

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

Extension method:

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



回答3:


So, here's the thing. There's language integrated query (Linq), and then there's yielded enumeration (a.k.a. Iterators).

Linq allows you to define an expression tree that is later executed by something that may or may not be C# (for example the expression could be translated into a SQL query). If you're writing linq there is a good chance your query provider (the thing that does the expression translation) does not support exception handling (much less whatever you're doing that throws exceptions).

Interators on the other hand (or "linq to objects") just ends up executing in C#, so you can just go wild with your exception handling.

For example w/ linq to objects you can do this:

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

If you're indeed doing linq to objects, and you just want to skip elements where your source IEnumerable threw an exception check out Vladimir Gondarev's answer.

The important thing to understand however, is that the anonymous function we just passed to that Select call is not an Expression (an uncompiled expression tree), it is a Func (a delegate pointing to compiled c# code), which means that it will run in the .Net process, even if we replaced myList with a linq to entities table (or some other linq provider). The reason for this is that the C# expression syntax does not support blocks, nor does it support try-catch. Unsurprisingly, given that, the SQL-style Linq statements (from xxx select yyy) also do not support try-catch blocks.

However, just because the C# expression syntax doesn't support it doesn't mean you can't do it. But, to be clear, I do not recommend doing this because I highly doubt there is a QueryProvider in existence that supports it (aside from the linq to objects provider). For the curious here's how you would create a lambda expression that contains a try-catch block.

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

So when somebody implements a QueryProvider backed by a store that supports exception handling, you could use this.




回答4:


It depends what exactly you are trying to achieve:

  1. Return the list except "broken" items.

    • You can try using a Where clause to check and filter them out:

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

      Apparently you need to implement IsValidNode which should check for null, if it can throw an InvalidObjectException (not sure if you can easily detect it but you can always wrap it in a try-catch block) and Id equality. Like that:

      private bool IsValidNode(Child c, int id)
      {
          if (x == null) return false;
          try {
              return c.Id == id;
          }
          catch (InvalidObjectException)
          {
      
          }
          return false;
      }
      
  2. Return an empty list if there are any broken items.

    • Just wrap the entire thing in a try-catch block


来源:https://stackoverflow.com/questions/19462764/exception-handling-in-linq-queries

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!