Reducing using/foreach/using nesting with a helper extension Problem statement

There are sometimes foreach scenarios that require deep nesting due to multiple disposable objects involved that look like this:


using(..)
{
    foreach(..)
    {
        using(..)
        {

        }
    }
}



or a real-world example from one of my applications:


using (_logger.BeginScope().CorrelationHandle("TestBundle").AttachElapsed())
using (Disposable.Create(() =&gt;
{
    foreach (var item in cache.Values) item.Dispose();
}))
{
    _logger.Log(Abstraction.Layer.Service().Meta(new { TestBundleFileName = testBundle.FileName }));
    foreach (var current in tests)
    {
        using (_logger.BeginScope().CorrelationHandle("TestCase").AttachElapsed())
        {
            // body
        }
    }
}



I find this code is very ugly and there is virtually no other way of writing this (or it's just me and I cannot think of any). 



Suggested solution

So, in order to make it prettier I created the Using extension that encapsulates the outer and inner usings within the loop.

Its body is identical to the using/foreach/using pattern I'd like to get rid of elsewhere:

public static class EnumerableExtensions
{   
    public static IEnumerable&lt;(TItem Current, TInner Context)&gt; Using&lt;TItem, TOuter, TInner&gt;
    (
        this IEnumerable&lt;TItem&gt; source,
        TOuter outer,
        Func&lt;TItem, TInner&gt; inner
    )
    {
        using (new Disposer&lt;TOuter&gt;(outer))
        {
            foreach (var item in source)
            {
                using (var context = new Disposer&lt;TInner&gt;(inner(item)))
                {
                    yield return (item, context);
                }
            }
        }
    }   
}


This is using a helper struct for disposing TOuter and TInner

public readonly struct Disposer&lt;T&gt; : IDisposable
{
    public Disposer(T value)
    {
        Value = value;
    }

    public T Value { get; }

    public void Dispose()       
    {
        if (Value is IDisposable disposable)
        {
            disposable.Dispose();
        }
        else
        {
            foreach (var property in typeof(T).GetProperties())
            {
                if (property.GetValue(Value) is IDisposable disposableProperty)
                {
                    disposableProperty.Dispose();
                }
            }
        }
    }

    public static implicit operator T(Disposer&lt;T&gt; disposer) =&gt; disposer.Value;
}


And here is the Disposable helper:

public class Disposable : IDisposable
{
    private readonly Action _dispose;

    private Disposable(Action dispose)
    {
        _dispose = dispose;
    }

    public static IDisposable Empty =&gt; new Disposable(() =&gt; { });

    public static IDisposable Create(Action dispose)
    {
        return new Disposable(dispose);
    }

    public void Dispose()
    {
        _dispose();
    }
}


Example usage

Now, when I have such a scenario, I can put everything inside the loop:

void Main()
{
    var numbers = new[] { 1, 2, 3 };

    foreach (var (item, context) in numbers.Using
    (
        outer: Disposable.Create (() =&gt; Console.WriteLine("Outer disposed.")), 
        inner: _ =&gt; Disposable.Create (() =&gt; Console.WriteLine("Inner disposed.")))
    )
    {
        item.Dump();
        context.Dump();
    }
}


Then, when I refactor the previous ugly piece of code with this extension it turns into this:


foreach (var (current, context) in tests.Using
(
    outer: new 
    {
        Scope = _logger.BeginScope().CorrelationHandle("TestBundle").AttachElapsed(),
        CleanUp = Disposable.Create(() =&gt;
        {
            foreach (var item in cache.Values) item.Dispose();
        })
    }, 
    inner: _ =&gt; _logger.BeginScope().CorrelationHandle("TestCase").AttachElapsed())
)
{
    // body
}



Questions


What do you think of this helper? 
Would you say the code is now easier to read?
Can the Using extension or the Disposer&lt;T&gt; be improved?
I'd argue that no, this does not make the code easier to understand. Instead of a combination of familiar general-purpose constructs (using and foreach), you now have an undocumented custom special-purpose construct Using(outer, inner) that must be understood before one can make sense of the code. This use-case seems too uncommon to be worth that trade-off.

The main problem I have with the original piece of code is that it looks like a 'camel' because of the Disposable.Create hump at the start. Your alternative is still a camel, just with a bigger hump at the front and a flatter one at the back. With a DisposeAll extension method, you could turn it into a dromedary. It's still a tall hump, but now the shape actually matches its single-loop nature:

using (_logger.BeginScope().CorrelationHandle("TestBundle").AttachElapsed())
using (Disposable.Create(() =&gt; cache.Values.DisposeAll()))
{
    _logger.Log(Abstraction.Layer.Service().Meta(new { TestBundleFileName = testBundle.FileName }));
    foreach (var current in tests)
    {
        using (_logger.BeginScope().CorrelationHandle("TestCase").AttachElapsed())
        {
            // TODO
        }
    }
}


Another thing you can do is to move the loop body to a separate method or local function.