MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Saturday, March 21, 2009

Why I don’t use OpenWeb()

This has come up in various conversations recently so I figured I’d write up a short post about it.  When trying to obtain an SPWeb object there are a couple of different options available using members of an SPSite instance.  The first, and more common, is the OpenWeb() method and the second is using the AllWebs[] property collection.  Here’s the problem I have with OpenWeb() (specifically the overload that takes no arguments) – consider the following code:

string url = "http://portal/sites/ActualSite/ChildWeb/GrandChildWeb";
using (SPSite site = new SPSite(url))
using (SPWeb web = site.OpenWeb())
{
    ...
}

In the above code if both ChildWeb and GrandChildWeb actually exist then the OpenWeb() call will return the GrandChild web.  However, lets assume that GrandChildWeb doesn’t exist (or perhaps it was spelled incorrectly), in that case what you will get for the web variable will be ChildWeb (assuming it exists).  The problem is that the user will not get any error indicating that the URL provided was invalid – it simply returns back the first valid web in the URL hierarchy.  In some cases this is exactly what you want (this is common when the URL you have is that of a List or Folder) but generally this is not going to be the case.

Now consider the following code where “Utilities.GetServerRelativeUrl” is a helper method that returns back the server relative URL:

string url = "http://portal/sites/ActualSite/ChildWeb/GrandChildWeb";
using (SPSite site = new SPSite(url))
using (SPWeb web = site.OpenWeb(Utilities.GetServerRelativeUrl(url), true))
{
    ...
}

In the above code if GrandChildWeb does not exist then an ArgumentException will be thrown thus ensuring that the code does not now inadvertently operate on the wrong SPWeb object.  Alternatively you could do the exact same thing using the AllWebs property collection as shown below:

string url = "http://portal/sites/ActualSite/ChildWeb/GrandChildWeb";
using (SPSite site = new SPSite(url))
using (SPWeb web = site.AllWebs[Utilities.GetServerRelativeUrl(url)])
{
    ...
}

I actually prefer using this method when the URL I have is explicitly expected to be the URL of a web.  This helps me during code reviews to do a quick search for all OpenWeb calls thus allowing me to focus my attention on how and where the URL parameter is coming from (so I use OpenWeb() with no arguments whenever I have an arbitrary URL that points to a resource below a web and not the web itself and I use AllWebs for everything else).

So I know what you’re thinking: “But Gary, won’t using a property collection result in all the webs being opened so isn’t using AllWebs less performant?”.  Actually, no, that’s not how AllWebs works – internally the indexer for AllWebs makes a call to the SPSite.OpenWeb(string, bool) method passing in the server relative URL and true to ensure an exact match.  This results in an ArgumentException if the URL specified does not correspond to a web.

In conclusion, use AllWebs when the URL you have is expected to correspond to a web and use OpenWeb when the URL corresponds to a list, folder, or file.

8 comments:

Raymond said...

Great stuff, Gary!

soerennielsen said...

Interesting article.

I seem to remember (from the SPS2003 days) that OpenWeb can be performed if you have access to the web you are trying to open.

In contrast AllWebs requires you to have high (admin?) permissions on the SPSite object.

It's at least 5 years ago that I worked on that codebase, but the same might still hold true.

Gary Lapointe said...

If you disassemble the AllWebs property you'll see that it's just calling OpenWeb so I believe that if you will not require any sort of elevated permissions.

Anonymous said...

From where we will get this helper method Utilities.GetServerRelativeUrl

Gary Lapointe said...

This is what I use:

internal static string GetServerRelUrlFromFullUrl(string url)
{
int index = url.IndexOf("//");
if ((index < 0) || (index == (url.Length - 2)))
{
throw new ArgumentException();
}
int startIndex = url.IndexOf('/', index + 2);
if (startIndex < 0)
{
return "/";
}
string str = url.Substring(startIndex);
if (str.IndexOf("?") >= 0)
str = str.Substring(0, str.IndexOf("?"));

if (str.IndexOf(".aspx") > 0)
str = str.Substring(0, str.LastIndexOf("/"));

if ((str.Length > 1) && (str[str.Length - 1] == '/'))
{
return str.Substring(0, str.Length - 1);
}
return str;
}

Anonymous said...

Gary, you rule!

Great post, even though Bernd still doesn't believe it!

Yohan Belval said...

According to MSDN (I'm not saying they're always right...) using the SPSite.OpenWeb(string, boolean) override function will do just that: Require an exact url to open the SPWeb. I'm guessing that if you pass a url of a non-existing site, it will throw an exception.

p.s.: Thanks for the info Gary! Always great to read your posts.

Gary Lapointe said...

Correct - I'm pretty sure I mentioned that within the article - the main issue is using OpenWeb with no arguments (by using AllWebs it makes it easier for me to scan my code and other peoples code for potential issues - so I make OpenWeb an exception and evaluate each one closely when doing code reviews).