Jader Dias
Jader Dias

Reputation: 90475

Is it possible to handle exceptions within LINQ queries?

Example:

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a));

How to make it work even if it throws exceptions? Like a try catch block with a default value case an exceptions is thrown...

Upvotes: 45

Views: 34566

Answers (10)

Herman Schoenfeld
Herman Schoenfeld

Reputation: 8724

I believe this is the correct answer since it allows you to handle the troublesome item and it is filtered from the end result.


public static class IEnumerableExtensions {

    public static IEnumerable<TResult> SelectWithExceptionHandler<T, TResult>(this IEnumerable<T> enumerable, Func<T, TResult> func, Action<T, Exception> handler)
        => enumerable
            .Select(x => {
                try {
                    return (true, func(x));
                } catch (Exception error) {
                    handler(x, error);
                }
                return default;
            })
            .Where(x => x.Item1)
            .Select(x => x.Item2);

}

Upvotes: 0

Rm558
Rm558

Reputation: 4992

wrap ThisMethodMayThrowExceptions with a new function,

myEnumerable.Select(a =>         ThisMethodMayThrowExceptions(a)); //old
myEnumerable.Select(a => tryCall(ThisMethodMayThrowExceptions,a)); //new

it's a generic function, with try catch inside.

T2 tryCall<T1, T2>(Func<T1, T2> fn, T1 input, T2 exceptionValue = default)
{
    try
    {
        return fn(input);
    }
    catch
    {
        return exceptionValue;
    }
}

var numbers = new [] {"1", "a"};
numbers.Select(n => tryCall(double.Parse, n));             //1, 0
numbers.Select(n => tryCall(double.Parse, n, double.NaN)); //1, NaN

Upvotes: 0

shtse8
shtse8

Reputation: 1365

/// <summary>
/// Catch the exception and then omit the value if exception thrown.
/// </summary>
public static IEnumerable<T> Catch<T>(this IEnumerable<T> source, Action<Exception> action = null)
{
    return Catch<T, Exception>(source, action);
}


/// <summary>
/// Catch the exception and then omit the value if exception thrown.
/// </summary>
public static IEnumerable<T> Catch<T, TException>(this IEnumerable<T> source, Action<TException> action = null) where TException : Exception
{
    using var enumerator = source.GetEnumerator();
    while(true)
    {
        T item;
        try
        {
            if (!enumerator.MoveNext())
                break;
            item = enumerator.Current;
        }
        catch (TException e)
        {
            action?.Invoke(e);
            continue;
        }
        yield return item;
    }
}

/// <summary>
/// Catch the exception and then return the default value.
/// </summary>
public static IEnumerable<T> Catch<T>(this IEnumerable<T> source, Func<Exception, T> defaultValue)
{
    return Catch<T, Exception>(source, defaultValue);
}

/// <summary>
/// Catch the exception and then return the default value.
/// </summary>
public static IEnumerable<T> Catch<T, TException>(this IEnumerable<T> source, Func<TException, T> defaultValue) where TException : Exception
{
    using var enumerator = source.GetEnumerator();
    while(true)
    {
        T item;
        try
        {
            if (!enumerator.MoveNext())
                break;
            item = enumerator.Current;
        }
        catch (TException e)
        {
            item = defaultValue(e);
        }
        yield return item;
    }
}

Usage:

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(e => Console.WriteLine(e.Message));

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(e => default);

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch();

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(((InvalidOperationException) e) => Console.WriteLine(e.Message));

myEnumerable.Select(a => ThisMethodMayThrowExceptions(a)).Catch(((InvalidOperationException) e) => default);

Upvotes: 2

Artem Prokudin
Artem Prokudin

Reputation: 13

I've created small library for this purposes. It's supported exception handling for Select, SelectMany and Where operators. Usage example:

var target = source.AsCatchable() // move source to catchable context
    .Select(v => int.Parse(v)) // can throw an exception
    .Catch((Exception e) => { /* some action */ }, () => -1) 
    .Select(v => v * 2)
    .ToArray();

which equivalet to

var target = source
    .Select(v => 
        {
            try
            {
                return int.Parse(v);
            }
            catch (Exception)
            {
                return -1; // some default behaviour 
            }
        })
    .Select(v => v * 2)
    .ToArray();

It's also possible to handle several types of exceptions

var collection = Enumerable.Range(0, 5)
                .AsCatchable()
                .Select(v =>
                {
                    if (v == 2) throw new ArgumentException("2");
                    if (v == 3) throw new InvalidOperationException("3");
                    return v.ToString();
                })
                .Catch((ArgumentException e) => { /*  */ }, v => "ArgumentException")
                .Catch((InvalidOperationException e) => { /*  */ }, v => "InvalidOperationException")
                .Catch((Exception e) => { /*  */ })
                .ToList();

Upvotes: 1

LeBaptiste
LeBaptiste

Reputation: 1186

I have come with a small extension when I quickly want to try/catch every iteration of an IEnumerable<T>

Usage

public void Test()
{
    List<string> completedProcesses = initialEnumerable
        .SelectTry(x => RiskyOperation(x))
        .OnCaughtException(exception => { _logger.Error(exception); return null; })
        .Where(x => x != null) // filter the ones which failed
        .ToList();
}

The extension

public static class OnCaughtExceptionExtension
{
    public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
    {
        foreach (TSource element in enumerable)
        {
            SelectTryResult<TSource, TResult> returnedValue;
            try
            {
                returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
            }
            catch (Exception ex)
            {
                returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
            }
            yield return returnedValue;
        }
    }

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler)
    {
        return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException));
    }

    public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler)
    {
        return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException));
    }

    public class SelectTryResult<TSource,TResult>
    {
        internal SelectTryResult(TSource source, TResult result, Exception exception)
        {
            Source = source;
            Result = result;
            CaughtException = exception;
        }

        public TSource Source { get; private set; }
        public TResult Result { get; private set; }
        public Exception CaughtException { get; private set; }
    }
}

We could eventually go a bit further by having a SkipOnException extension, accepting optionally an exception handler for example.

Upvotes: 6

THTP
THTP

Reputation: 1466

In case you need Expression instead of lambda function (e.g. when selecting from IQueryable), you can use something like this:

public static class ExpressionHelper
{
    public static Expression<Func<TSource, TResult>> TryDefaultExpression<TSource, TResult>(Expression<Func<TSource, TResult>> success, TResult defaultValue)
    {
        var body = Expression.TryCatch(success.Body, Expression.Catch(Expression.Parameter(typeof(Exception)), Expression.Constant(defaultValue, typeof (TResult))));
        var lambda = Expression.Lambda<Func<TSource, TResult>>(body, success.Parameters);

        return lambda;
    }
}

Usage:

[Test]
public void Test()
{
    var strings = new object [] {"1", "2", "woot", "3", Guid.NewGuid()}.AsQueryable();
    var ints = strings.Select(ExpressionHelper.TryDefaultExpression<object, int>(x => Convert.ToInt32(x), 0));
    Assert.IsTrue(ints.SequenceEqual(new[] {1, 2, 0, 3, 0}));
}

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1500785

Call a projection which has that try/catch:

myEnumerable.Select(a => TryThisMethod(a));

...

public static Bar TryThisMethod(Foo a)
{
     try
     {
         return ThisMethodMayThrowExceptions(a);
     }
     catch(BarNotFoundException)
     {
         return Bar.Default;
     }
}

Admittedly I'd rarely want to use this technique. It feels like an abuse of exceptions in general, but sometimes there are APIs which leave you no choice.

(I'd almost certainly put it in a separate method rather than putting it "inline" as a lambda expression though.)

Upvotes: 25

esteewhy
esteewhy

Reputation: 1310

A variation of Stefan's solution for comprehension syntax:

from a in myEnumerable
select (new Func<myType>(() => {
    try
    {
        return ThisMethodMayThrowExceptions(a));
    }
    catch(Exception)
    {
        return defaultValue;
    }
}))();

Although, it "smells" too, but still this approach can sometimes be used for running code with side-effects inside expression.

Upvotes: 4

Nathan Taylor
Nathan Taylor

Reputation: 24606

When dealing with LINQ you'll commonly find scenarios where your expression could produce undesired side effects. As Jon said, the best way to combat these sort of problems is to have utility methods your LINQ expression can use that will handle these gracefully and in a fashion that won't blow up your code. For example, I have a method I've had to use time to time which wraps a TryParse to tell me if something is a number. There are many other examples of course.

One of the limitations of the expression syntax is that there are a lot of things it can't do either gracefully or even at all without breaking execution out of the expression temporarily to handle a given scenario. Parsing a subset of items in an XML file is wonderful example. Try parsing a complex parent collection with child subsets from an XML file within a single expression and you'll soon find yourself writing several expression pieces that all come together to form the entire operation.

Upvotes: 2

Stefan Steinegger
Stefan Steinegger

Reputation: 64628

myEnumerable.Select(a => 
  {
    try
    {
      return ThisMethodMayThrowExceptions(a));
    }
    catch(Exception)
    {
      return defaultValue;
    }
  });

But actually, it has some smell.

About the lambda syntax:

x => x.something

is kind of a shortcut and could be written as

(x) => { return x.something; }

Upvotes: 46

Related Questions