Extending SharePoint: Checking if a List exists
A common problem when working with the SharePoint Object Model is getting a handle to a list. Very often we find ourselves writing this:
1: try
2: {
3: using (var web = site.OpenWeb())
4: {
5: if (web != null)
6: {
7: try
8: {
9: var list = web.Lists[ListTitle];
10: }
11: catch (ArgumentException)
12: {
13: web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
14: }
15: }
16: else
17: {
18: Console.WriteLine("Unable to open web site.");
19: }
20: }
21: }
22: catch (Exception ex)
23: {
24: Console.WriteLine(ex.Message);
25: }
This adds a list if it doesn't exist on the site. What a pain to have to write this over and over again. Okay, so step 1 might be to write a method on a helper class (usually a static class and static method) that would do it for you. I'm not a huge fan of helper classes as you eventually end up with the MonolithicHelperClass or MotherOfAllHelperClasses that pretty much does everything. That's not cool in my books.
Enter extension methods for SharePoint.
If you're building your web part or solution on .NET 3.0 or higher, you can make use of a feature that was introduced called extension methods. I won't get into the details of extension methods here but check out this post by Scott Guthrie on them and do some Googling if you want to know more. In a nutshell, extension methods let us extend existing classes not by subclassing them but by adding new methods to them. In the SharePoint world, extension methods are really useful due to a lot of the OM "workarounds" we face every day.
To create an extension method we need to do two things. First, create a static class to hold them. This can be named anything you want. Then include a static method. The first parameter to the static method will be the class you're extending (in our case SPWeb) prefixed with the keyword "this". "this" tells the compiler that we're extending the type specified so whenver we encounter a method with this signature of the type, please run our code for us. Here's our code moved into an extension method:
1: public static class SPWebExtensions
2: {
3: public static bool ListExists(this SPWeb web, string listName)
4: {
5: try
6: {
7: var list = web.Lists[listName];
8: return true;
9: }
10: catch (ArgumentException)
11: {
12: return false;
13: }
14: }
15: }
Not bad. So now we can use it like this:
1: try
2: {
3: using (var web = site.OpenWeb())
4: {
5: if (web != null)
6: {
7: if(!web.ListExists(ListTitle))
8: {
9: web.Lists.Add(ListTitle, ListDescription, SPListTemplateType.GenericList);
10: }
11: }
12: else
13: {
14: Console.WriteLine("Unable to open web site.");
15: }
16: }
17: }
18: catch (Exception ex)
19: {
20: Console.WriteLine(ex.Message);
21: }
However I'm still not happy with the try/catch statement in our extension method. Sure it works but it's expensive always throwing an exception when a list doesn't exist. Inside our extension method, we have access to all public methods and properties of the instance of the object we're extending so why not iterate through the list, checking to see if the name matches up and returning our boolean value instead?
1: public static class SPWebExtensions
2: {
3: public static bool ListExists(this SPWeb web, string listName)
4: {
5: var lists = web.Lists;
6: foreach (SPList list in lists)
7: {
8: if(list.Title.Equals(listName))
9: return true;
10: }
11: return false;
12: }
13: }
This is cleaner and doesn't throw an exception if the list isn't found. Some would argue that iterating through the entire list is expensive but really people, if you have *that* many lists on your site you might want to consider a bit of a content/architectural review first before blaming the code techniques presented here.
FWIW, there is a nice library of SharePoint extensions available on CodePlex here. They have an extension method that will check for a list however I'm presenting this solution here because you may not want to commit to an entire library for a single need like we have here. Their ListExists method extends the SPWeb object, but also makes use of their own extension to the SPListCollection and some LINQ magic. Feel free to use that solultion as there are a lot of great extensions available there.
Unfortunately there's not much hope in the future for fixes to the Object Model so we continue to add our own extensions as we see fit and the need arises. Eventually something will exist in the base system that you can retire your own RYO code but for now this is a pretty good compromise IMHO. YMMV.