Обработка исключений в выражении LINQ

У меня есть простое выражение LINQ, например:

newDocs = (from doc in allDocs
       where GetDocument(doc.Key) != null
       select doc).ToList();

Проблема заключается в том, что GetDocument() может генерировать исключение. Как я могу игнорировать все doc-элементы, где GetDocument (doc.Key) == null или генерирует исключение?

Тот же код в старой школе выглядит следующим образом:

foreach (var doc in allDocs)
            {
                try
                {
                    if (GetDocument(doc.Key) != null) newDocs.Add(doc);
                }
                catch (Exception)
                {
                    //Do nothing...
                }
            }

Ответы

Ответ 1

allDocs.Where(doc => {
    try {
        return GetDocument(doc.Key) != null;
    } catch {
        return false;
    }
}).ToList();

Я не уверен, что это возможно с использованием синтаксиса понимания запроса, кроме как через какое-то барочное злодеяние, вроде этого:

newDocs = (from doc in allDocs
           where ((Predicate<Document>)(doc_ => {
               try {
                   return GetDocument(doc_.Key) != null;
               } catch {
                   return false;
               }
           }))(doc)
           select doc).ToList();

Ответ 2

Вы можете переместить весь блок try catch и GetDocument на другой метод

Document TryGetDocument(string key)
{
         try
         {
            if (GetDocument(doc.Key) != null) 
              return doc;
         }
         catch (Exception)
         {
             return null;
         }
     return null;
}

а затем используйте эту функцию в вашем запросе -

newDocs = (from doc in allDocs
       where TryGetDocument(doc.Key) != null
       select doc).ToList();

Это упростит ваш запрос и будет легко читаться.

Ответ 3

Расширение linq можно записать, чтобы пропустить все элементы, которые вызывают исключение. fooobar.com/questions/257645/...

 public static IEnumerable<T> CatchExceptions<T> (this IEnumerable<T> src, Action<Exception> action = null) {
        using (var enumerator = src.GetEnumerator()) {
            bool next = true;

            while (next) {
                try {
                    next = enumerator.MoveNext();
                } catch (Exception ex) {
                    if (action != null) {
                        action(ex);
                    }
                    continue;
                }

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

Пример:

ienumerable.Select(e => e.something).CatchExceptions().ToArray()

ienumerable.Select(e => e.something).CatchExceptions((ex) => Logger.Log(ex, "something failed")).ToArray()

разместив это здесь, если кто-то еще найдет этот ответ первым.

Ответ 4

Вы пробовали Expression.TryCatch

IEnumerable<string> allDocs = new List<string>();
var newDocs = (from doc in allDocs
                    where Expression.TryCatch(
                          Expression.Block(GetDocument(doc.key)),
                          Expression.Catch(typeof(Exception),null)) != null                                 
                          select doc).ToList();

TryExpression msdn

Ответ 5

Напишите свой собственный метод. MyGetDocument( ), который будет обрабатывать исключение и вызывать его из LINQ.

newDocs = (from doc in allDocs
       where MyGetDocument(doc.Key) != null
       select doc).ToList();