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.

Related Articles:

About Keith Dahlby

I'm a .NET developer, Git enthusiast and language geek from Cedar Rapids, IA. I work as a software guru at J&P Cycles and studied Human-Computer Interaction at Iowa State University.
This entry was posted in IDisposable, LINQ, LINQ to SQL. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.codebork.com/ Alastair Smith

    Wow, that’s a really elegant solution!

  • http://blogger.forgottenskies.com Steve

    When I call result.Count on a similiar example of above I get an error:

    System.ObjectDisposedException: Cannot access a disposed object.
    Object name: ‘DataContext accessed after Dispose.’.

    Isn’t this code to prevent that from occuring ?

  • http://blogger.forgottenskies.com Steve

    I was incorrect on my last post – it is working as expected.

    I did a Count(), then attempted to loop over the collection in my test – after the count call it was disposed.

  • http://blogger.forgottenskies.com Steve

    The code above – does require to be used with a ‘AsQueryable’ to get it to build.

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

  • http://www.hanselman.com Scott Hanselman

    I love this. I’ve forwarded it to the C# designers.

  • http://www.lostechies.com/members/dahlbyk/default.aspx Keith Dahlby

    @Steve ~

    You are indeed correct. However, that workaround doesn’t have the desired effect of allowing additional manipulation of the original query with its original provider. Instead, we’ll just get an IQueryable that uses LINQ to Objects under the hood – we might as well just return IEnumerable!

    I have a post in the works with an actual fix for IQueryable.

    Cheers ~
    Keith