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.

8 Comments

  • Did you measure that? You know, "measure twice, cut once".

    I mean, tradeoff how many times do I query for a list that does not exist vs. querying for one that does exist but I'd bet my lower back that the iterator is slower than the exception even for a fairly small number of lists (it needs to pull all the metadata of the lists in memory, does it?)


    I hate extension methods, though in this case I'd agree it's clean.

  • @Fire Snake: Nope, I totally did not measure this. So yes, if you ran it on a site with say 100 (or even 1000) lists would there be a difference? The Lists[string] accessor needs to load everything into memory whereas the foreach only loads one list at a time and dumps the list metadata after each point of checking the Title property. I think it's a matter of preference but maybe someone can do the timings and prove that exceptions are faster. IMHO I really don't like relying on exceptions. To me, exceptions are "exceptional". The absence of a list should not be considered an exception. It simply doesn't exist. There are people that consider the absence of things to be "exceptional" but I don't hence why I prefer the foreach (but I could be swayed the other way if the numbers were really off, say 1s compared to 10ms).

  • One consideration when working on SPExLib has been trying to ensure that the methods behave consistently with the equivalent techniques in the object model. For example, the SPListCollection string indexer uses an InvariantCultureIgnoreCase comparison, buried in MS.SP.Utilities.SPUtilityInternal.StsCompareStrings. I factored this logic into a SPList.NameEquals(string) extension method. And even without our LINQ "magic", you can simplify your method quite a bit with OOTB LINQ:

    return lists.Cast().Any(list => list.NameEquals(listName));

    And a minor clarification: extension methods require VS2008 and either .NET 3.5 (System.Core) or a custom-defined System.Runtime.CompilerServices.ExtensionAttribute.

    Cheers ~
    Keith

  • The first release of SPExLib used the Try/catch version but after doing some research on performance I've found out that using the "Linq way" as Keith shows in his comment is far more better, except in the case when we expect in the case when we can expect the list to actually exist. That's another reason why it's implemented like that in SPExLib.
    The Linq way will give you a performance penalty when cold starting the app, since it has to load some additional assemblies, but this can be ignored since I assume if you build an extension method you will use it more than once in an app.

    /WW

  • Thanks for posting the code snippets. I had to go back and check my code, because I've written almost the exact same list exists method in code a few months back. Your code might benefit from strong typing the var to a ListsCollection. My ListExists method also returns the ServerRelativeUrl as a string out parameter as that is often what I am looking for when needing to perform the check.
    WW, is there a benefit other than coding preference to the LINQ code? I'm not sure I'd want the performance delay tradeoff with the loading and casting when Bill's code is pretty simple already.

  • @fire snake; it's actually counter-intuitive. catching the exception is actually slower. Whether or not you attempt to find a list by name using SPWeb.Lists['xxx'] and catch, or iterate through the OM is still loading the entire collection BEFORE it processes your request. With the Exception catch the OM has the additional overhead of decorating and piping up the Exception.

    I've got some code around here somewhere in which I actually did benchmarks on list retrieval. I'll dig it up tonight and post it.

  • @Tom: I did some pretty basic test (not as thorough as Scott) where I found out that:
    1) When doing it many times, Linq almost every time outperform the try/catch
    2) Linq is slower on the first calls (cold start issues) and when doing it few times
    3) When you are more likely to find the list try/catch is faster on fewer request and much slower if you unlikely to find it.
    I did only measure time, not memory or anything else, which may be in order to do.
    /WW

  • public static bool ListExists(this SPWeb web, string listName)
    {
    var lists = web.Lists;
    return lists.Cast().Any(list => list.Title.Equals(listName));
    }

Comments have been disabled for this content.