Los Techies : Blogs about software and anything tech!

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.

Kick It on DotNetKicks.com
Posted Jul 23 2009, 04:29 AM by Keith Dahlby

Comments

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - July 22-24, 2009
on 07-23-2009 7:11 AM

Daily tech links for .net and related technologies - July 22-24, 2009 Web Development Simplify calling

Reflective Perspective - Chris Alcock » The Morning Brew #397 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #397
on 07-24-2009 3:42 AM

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #397

progg.ru wrote Using IDisposables with LINQ - Solutionizing .NET
on 07-24-2009 4:26 AM

Thank you for submitting this cool story - Trackback from progg.ru

Alastair Smith wrote re: Using IDisposables with LINQ
on 07-24-2009 9:07 AM

Wow, that's a really elegant solution!

Steve wrote re: Using IDisposables with LINQ
on 07-27-2009 10:31 AM

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 ?

Steve wrote re: Using IDisposables with LINQ
on 07-27-2009 10:40 AM

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.

Steve wrote re: Using IDisposables with LINQ
on 07-27-2009 11:13 AM

The code above - does require to be used with a 'AsQueryable' to get it to build.

ie.

IQueryable<MyType> MyFunc(string myValue)

{

   return (from dc in new MyDataContext().Use()

          from row in dc.MyTable

          where row.MyField == myValue

          select row).AsQueryable();

}

Scott Hanselman wrote re: Using IDisposables with LINQ
on 07-28-2009 9:52 PM

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

Keith Dahlby wrote re: Using IDisposables with LINQ
on 07-29-2009 3:19 PM

@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<MyType>!

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

Cheers ~

Keith

Introducing LazyLinq: Overview - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software, programming and anything tech! wrote Introducing LazyLinq: Overview - Solutionizing .NET (Keith Dahlby) - Los Techies : Blogs about software, programming and anything tech!
on 08-06-2009 2:54 PM

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

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems