Skip to content

Instantly share code, notes, and snippets.

@mbdavid
Created December 29, 2016 23:56
Show Gist options
  • Save mbdavid/6c693fc28ededabc8683de2f4d1364c9 to your computer and use it in GitHub Desktop.
Save mbdavid/6c693fc28ededabc8683de2f4d1364c9 to your computer and use it in GitHub Desktop.
Catch in IEnumerable Yield
// why this do not work?
public IEnumerable<T> Find(Query query)
{
while(true)
{
try
{
// FindWrap returns IEnumerable using yield return
return FindWrap(query);
}
catch(IndexNotFoundException ex)
{
// never run this!!
this.EnsureIndex(ex);
}
catch(Exception ex)
{
// doesn't works here too
}
finaly
{
// where it's ok
}
}
}
@mkosieradzki
Copy link

You can start with something like that:

        sealed class SafeEnumerator<T> : IEnumerator<T>
        {
            private IEnumerator<T> inner;

            public SafeEnumerator(IEnumerator<T> inner)
            {
                if (inner == null)
                    throw new ArgumentNullException(nameof(inner));

                this.inner = inner;
            }

            public T Current
            {
                get
                {
                    try
                    {
                        return Current;
                    }
                    catch
                    {
                        //Do your stuff
                    }
                }
            }

            object IEnumerator.Current => Current;
            public void Dispose() => inner.Dispose();
            public bool MoveNext() => inner.MoveNext();

            public void Reset() => inner.Reset();
        }

        sealed class SafeEnumerable<T> : IEnumerable<T>
        {
            private IEnumerable<T> inner;

            public SafeEnumerable(IEnumerable<T> inner)
            {
                if (inner == null)
                    throw new ArgumentNullException(nameof(inner));

                this.inner = inner;
            }

            public IEnumerator<T> GetEnumerator() => new SafeEnumerator<T>(inner.GetEnumerator());
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }

@mkosieradzki
Copy link

or just trivial if you don't need to stream the results:

return FindWrap(query).ToArray();

@mkosieradzki
Copy link

I have improved my snippet a bit:

        sealed class SafeEnumerator<T> : IEnumerator<T>
        {
            private IEnumerator<T> inner;
            private Action<Exception> exceptionHandler;

            public SafeEnumerator(IEnumerator<T> inner, Action<Exception> exceptionHandler)
            {
                if (inner == null)
                    throw new ArgumentNullException(nameof(inner));
                if (exceptionHandler == null)
                    throw new ArgumentNullException(nameof(exceptionHandler));

                this.inner = inner;
                this.exceptionHandler = exceptionHandler;
            }

            public T Current => inner.Current;

            object IEnumerator.Current => Current;
            public void Dispose() => inner.Dispose();
            public bool MoveNext()
            {
                try
                {
                    return inner.MoveNext();
                }
                catch (Exception ex)
                {
                    exceptionHandler(ex);
                }
                return inner.MoveNext();
            }
            public void Reset() => inner.Reset();
        }

        sealed class SafeEnumerable<T> : IEnumerable<T>
        {
            private IEnumerable<T> inner;
            private Action<Exception> exceptionHandler;

            public SafeEnumerable(IEnumerable<T> inner, Action<Exception> exceptionHandler)
            {
                if (inner == null)
                    throw new ArgumentNullException(nameof(inner));
                if (exceptionHandler == null)
                    throw new ArgumentNullException(nameof(exceptionHandler));

                this.inner = inner;
                this.exceptionHandler = exceptionHandler;
            }

            public IEnumerator<T> GetEnumerator() => new SafeEnumerator<T>(inner.GetEnumerator(), exceptionHandler);
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }

Now you can use it this way:

return new SafeEnumerable(FindWrap(query), ex =>
{
    var indexNotFound = ex as IndexNotFoundException;
    if (indexNotFound != null)
        this.EnsureIndex(indexNotFound);
    else
        throw new Exception("....", ex);
});

@mbdavid
Copy link
Author

mbdavid commented Dec 30, 2016

I wrote this:

while (true)
{
    var enumerable = _engine.Value.Find(_name, query, skip, limit);
    var enumerator = enumerable.GetEnumerator();

    for (bool more = true; more;)
    {
        // Catch exceptions only on executing/resuming the iterator function
        try
        {
            more = enumerator.MoveNext();
        }
        catch (IndexNotFoundException ex)
        {
            this.EnsureIndex(ex);

            enumerable = _engine.Value.Find(_name, query, skip, limit);
            enumerator = enumerable.GetEnumerator();
            continue;
        }

        // executing all includes in BsonDocument
        foreach (var action in _includes)
        {
            action(enumerator.Current);
        }

        // get object from BsonDocument
        var obj = _mapper.ToObject<T>(enumerator.Current);

        yield return obj;
    }

    if (enumerator != null) enumerator.Dispose();
}

Work for IEnumerable/Catch exception but it's not valid in this case: locking problems (as #399).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment