Возврат доходности из блока try / catch

Как описал Эрик Липперт в в этой статье, yield return нельзя использовать в try/catch пунктах.

Есть ли хороший способ получить что-то вроде этого без необходимости писать свои собственные IEnumerator вручную:

public IEnumerable<Data> GetData()
{
    var transaction = Session.BeginTransaction());
    try 
    {
        IQuery q = CreateQuery(session);

        foreach (var result in q.Enumerable())
            yield return ProjectResult(result);  // <-- doesn't work

        session.Commit();
    }
    catch (Exception ex)
    {
        transaction.Rollback();
        throw;
    }
    finally
    {
        transaction.Dispose();
    }
}

person Groo    schedule 30.08.2011    source источник


Ответы (2)


Я бы просто изменил логику обработки транзакций следующим образом:

public IEnumerable<Data> GetData()
{
    var transaction = Session.BeginTransaction();
    bool rollback = true;
    try 
    {
        IQuery q = CreateQuery(session);

        foreach (var result in q.Enumerable())
        {
            yield return ProjectResult(result);
        }

        rollback = false;
        session.Commit();
    }
    finally
    {
        if (rollback)
        {
            transaction.Rollback();
        }
        transaction.Dispose();
    }
}

Или, если ваша транзакция поддерживает идею «удаление означает откат, если он не зафиксирован»:

public IEnumerable<Data> GetData()
{
    using (var transaction = Session.BeginTransaction();
    {
        IQuery q = CreateQuery(session);

        foreach (var result in q.Enumerable())
        {
            yield return ProjectResult(result);
        }

        // Commits the tnrasaction, so disposing it won't roll it back.
        session.Commit();
    }
}
person Jon Skeet    schedule 30.08.2011
comment
Разве OP не сказал, что yield return не допускается в блоке try / catch? - person StriplingWarrior; 30.08.2011
comment
@Stripling, это не try / catch. - person Anthony Pegram; 30.08.2011
comment
@StriplingWarrior: Просто чтобы расширить комментарий Энтони: yield return разрешен в try / finally, но не в try / catch. - person Jon Skeet; 30.08.2011
comment
Ах, в упомянутой статье говорится: 3) Разрешить возврат yield в блоках try, которые имеют блоки finally, но не в том случае, если у них есть блоки catch. Понятно. - person StriplingWarrior; 30.08.2011
comment
Спасибо, первый пример - это умный обходной путь. Второй пример даже лучше, потому что NHibernate действительно выполняет неявные откаты при удалении без предварительной фиксации. - person Groo; 30.08.2011
comment
@Groo: Круто - это разумное поведение, если честно :) - person Jon Skeet; 30.08.2011
comment
@StriplingWarrior: чтобы прояснить комментарий Джона, Try / finally или Using будут работать только в том случае, если код, который использует перечислитель, правильно вызывает Dispose для него. Если код, который создает перечислитель, отказывается от него, не вызывая Dispose, код в блоке finally (или блоке finally, подразумеваемом с помощью) не будет выполняться. - person supercat; 30.08.2011

Рефактор

foreach (var result in q.Enumerable()) 
  yield return ProjectResult(result);

в отдельный метод и просто верните его результат.

person Andras Zoltan    schedule 30.08.2011
comment
Не могли бы вы добавить пример с показанным блоком try / catch? Я предполагаю, что ваше предложение не будет работать правильно из-за ленивой оценки; Предложение finally будет выполнено до повторения результатов, если нет yield return. - person Groo; 30.08.2011