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:
- LINQ’s query syntax depends on expressions;
usingblocks are statements. - When querying a sequence of
IDisposableobjects, there’s no easy way to ensure disposal after each element has been consumed. - Returning deferred queries from within a
usingstatement is often desired, but fails spectacularly.
There are possible work-arounds for each issue…
- Put the using statement in a method (named or anonymous) that is called from the query. See also: Thinking Functional: Using.
- Use a method that creates a dispose-safe iterator of the sequence, like AsSafeEnumerable().
- Refactor the method to inject the
IDisposabledependency, 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.
