Using IDisposables with LINQ

Objects that implement IDisposable are everywhere. The interface even gets its own language features (C#, VB, F#). However, LINQ throws a few wrenches into things:

  1. LINQ’s query syntax depends on expressions; using blocks are statements.
  2. When querying a sequence of IDisposable objects, there’s no easy way to ensure disposal after each element has been consumed.
  3. Returning deferred queries from within a using statement is often desired, but fails spectacularly.

There are possible work-arounds for each issue…

  1. Put the using statement in a method (named or anonymous) that is called from the query. See also: Thinking Functional: Using.
  2. Use a method that creates a dispose-safe iterator of the sequence, like AsSafeEnumerable().
  3. Refactor the method to inject the IDisposable dependency, as shown in the first part of Marc’s answer here.

But, as you might have guessed, I would like to propose a better solution. The code is really complex, so bear with me:

public static IEnumerable<T> Use<T>(this T obj) where T : IDisposable
{
    try
    {
        yield return obj;
    }
    finally
    {
        if (obj != null)
            obj.Dispose();
    }
}

That’s it. We’re turning our IDisposable object into a single-element sequence. The trick is that the C# compiler will build an iterator for us that properly handles the finally clause, ensuring that our object will be disposed. It might be helpful to set a breakpoint on the finally clause to get a better idea what’s happening.

So how can this simple method solve all our problems? First up: “using” a FileStream object created in a LINQ query:

var lengths = from path in myFiles
              from fs in File.OpenRead(path).Use()
              select new { path, fs.Length };

Since the result of Use() is a single-element sequence, we can think of from fs in something.Use() as an assignment of that single value, something, to fs. In fact, it’s really quite similar to an F# use binding in that it will automatically clean itself up when it goes out of scope (by its enumerator calling MoveNext()).

Next, disposing elements from a collection. I’ll use the same SharePoint problem that AsSafeEnumerable() solves:

var webs = from notDisposed in site.AllWebs
           from web in notDisposed.Use()
           select web.Title;

I find this syntax rather clumsy compared with AsSafeEnumerable(), but it’s there if you need it.

Finally, let’s defer disposal of a LINQ to SQL DataContext until after the deferred query is executed, as an answer to the previously-linked Stack Overflow question:

IQueryable<MyType> MyFunc(string myValue)
{
    return from dc in new MyDataContext().Use()
           from row in dc.MyTable
           where row.MyField == myValue
           select row;
}

void UsingFunc()
{
    var result = MyFunc("MyValue").OrderBy(row => row.SortOrder);
    foreach(var row in result)
    {
        //Do something
    }
}

The result of MyFunc now owns its destiny completely. It doesn’t depend on some potentially disposed DataContext – it just creates one that it will dispose when it’s done. There are probably situations where you would want to share a DataContext rather than create one on demand (I don’t use LINQ to SQL, I just blog about it), but again it’s there if you need it.

I’ve only started using this approach recently, so if you have any problems with it please share.

Advertisement

6 Responses to “Using IDisposables with LINQ”

  1. Introducing LazyLinq: Overview - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software, programming and anything tech! Says:

    […] recently posted an approach to dealing with IDisposable objects and LINQ. In the comments at LosTechies, Steve Gentile […]

  2. Introducing LazyLinq: Overview « Solutionizing .NET Says:

    […] recently posted an approach to dealing with IDisposable objects and LINQ. In the comments at LosTechies, Steve Gentile […]

  3. Hacking LINQ Expressions: Select With Index « Solutionizing .NET Says:

    […] use various techniques to broaden our expressive reach. I have already documented one such hack for managing IDisposable objects with LINQ, so I guess we can call this the second in an unbounded […]

  4. Scott Brickey Says:

    While adding some extra extension methods around this approach, I came across a scenario which results in failure to dispose of the objects… when using a short circuit LINQ method (Any, First, FirstOrDefault, etc)… the remaining objects in the initial array are not disposed… probably not an issue with the file example (since the disposal is only necessary if the file is being opened/processed)… but the AllWebs will materialize the entire resultset… so a short circuit would leave the remaining objects in memory.

    I wrote an article focusing on unit tests, which happened to include your code : http://sbrickey.com/Tech/Blog/Post/More_good_examples_of_Mock_Objects

    • Keith Dahlby Says:

      I appreciate the analysis in your post! One thing I should have pointed out above is that using objects wrapped in Use() (or AsSafeEnumerable()) is only safe within the context of enumeration over the sequence. Using FirstOrDefault(), for example, will return an instance that has already been disposed (while the rest of the sequence, ironically, won’t be).


Comments are closed.

%d bloggers like this: