Probably the ugliest scenario for properly disposing SPWeb objects is cleaning up when you enumerate SPSite.AllWebs. To simplify that task, I present a pair of LINQ-inspired extension methods:
public static void ForEach(this SPWebCollection webs, Action<SPWeb> action)
{
foreach (SPWeb web in webs)
{
action(web);
web.Dispose();
}
}
public static IEnumerable<TResult> Select<TResult>(this SPWebCollection webs, Func<SPWeb, TResult> action)
{
List<TResult> res = new List<TResult>(webs.Count);
webs.ForEach(w => res.Add(action(w)));
return res;
}
public static IEnumerable<TResult> Select<TResult>(this SPWebCollection webs, Func<SPWeb, TResult> selector)
{
foreach (SPWeb web in webs)
{
TResult ret = selector(web);
web.Dispose();
yield return ret;
}
}
Combined with lambda expressions, we can cleanly handle tasks that would ordinarily require more code and explicit disposal. Want a list of your web URLs?
var urls = site.AllWebs.Select(w => { return w.Url; });
How about updating a property on every web?
site.AllWebs.ForEach(w => { w.Properties["MyProp"] = DateTime.Now.ToString(); w.Properties.Update(); });
We can even leverage anonymous types:
var props = site.AllWebs.Select(w => { return new { w.Title, w.Url, MyProp = w.Properties["MyProp"] }; });
Speaking of LINQ and SharePoint, check out Adam Buenz‘s post on using LINQ’s IEnumerable.Cast<T> with SharePoint collections to get IQueryable support. And while using LINQ for filtering and such may be prettier, resist the urge to skip CAML altogether: there is definitely a performance advantage in filtering your SPListItemCollection with an SPQuery, especially for large lists. I can’t seem to find any hard data on this, so I nominate Waldek Mastykarz to investigate – his analyses of other performance topics were great.
Update 12/10/2008: New, improved Select! Discussed here.