SharePoint Internals – Hristo Pavlov’s Blog

20 October, 2008

A Very Fast Method to Get the Site Collection’s Web Structure

Filed under: SharePoint — Tags: , , , , , , , , — hristopavlov @ 5:09 am

This is how you can get the full structure a SharePoint site collection without the need of creating memory consuming and slow SPWeb objects.

Before all let me just mention that actually the fastest possible way would be if you connect directly to the Content Database and run a query in the Webs table for the given site collection Id.

Other than this all ways to get the structure using the SharePoint object model will have to rely on the unmanaged COM objects implemented in OWSSVR.DLL to get the info. The question is how we can do this without actually creating an SPWeb object. The standard way to get the structure would be to create a new SPSite and then to loop through all subwebs to get the job done but this takes a looong time and wastes memory.

The code below uses an SPSite object to initialize the information about all the webs in the site, with an unmanaged call to GetAllWebsOfSite() of OWSSVR, and then reads the returned info from the private fields of the SPSite.AllWebs member without creating SPWeb objects. You will (probably) need to have access to all those sites for this method to work. Here is the code:

public class SPWebEntry

{

    private string m_Url;

    private string m_Title;

    private Guid m_Id;

 

    internal List<SPWebEntry> SubWebsList = new List<SPWebEntry>();

    internal SPWebEntry ParentWebEntry = null;

 

    internal SPWebEntry(string url, string title, Guid id)

    {

        m_Url = url;

        m_Title = title;

        m_Id = id;

    }

 

    public string Url

    {

        get { return m_Url; }

    }

 

    public string Title

    {

        get { return m_Title; }

    }

 

    public Guid Id

    {

        get { return m_Id; }

    }

 

    public IEnumerable<SPWebEntry> SubWebs

    {

        get { return SubWebsList; }

    }

 

    public SPWebEntry ParentWeb

    {

        get { return ParentWebEntry; }

    }

}

 

public class SPQuickSiteStructure

{

    private SPWebEntry m_RootWeb;

 

    public SPWebEntry RootWeb

    {

        get { return m_RootWeb; }

    }

 

    public SPQuickSiteStructure(string siteCollectionUrl)

    {

        using (SPSite site = new SPSite(siteCollectionUrl))

        {

            // This will make an unmanaged call to get all webs including their Guid and Titles but will not create any SPWeb objects

            string[] allSubWebUrls = site.AllWebs.Names;

 

            FieldInfo fi = typeof(SPWebCollection).GetField(“m_strTitles”, BindingFlags.Instance | BindingFlags.NonPublic);

            string[] allSubWebTitles = (string[])fi.GetValue(site.AllWebs);

 

            fi = typeof(SPWebCollection).GetField(“m_guidWebIds”, BindingFlags.Instance | BindingFlags.NonPublic);

            Guid[] allSubWebGuids = (Guid[])fi.GetValue(site.AllWebs);

 

            m_RootWeb = new SPWebEntry(

                    allSubWebUrls[0],

                    allSubWebTitles[0],

                    allSubWebGuids[0]);

 

            SPWebEntry currWeb = RootWeb;

 

            for (int i = 1; i < allSubWebUrls.Length; i++)

            {

                SPWebEntry web = new SPWebEntry(

                    allSubWebUrls[i],

                    allSubWebTitles[i],

                    allSubWebGuids[i]);

 

                while (

                    currWeb != null &&

                    (web.Url.Length < currWeb.Url.Length ||

                     web.Url.Substring(0, currWeb.Url.Length) != currWeb.Url))

                {

                    currWeb = currWeb.ParentWeb;

                }

 

                web.ParentWebEntry = currWeb;

                if (currWeb != null) currWeb.SubWebsList.Add(web);

                currWeb = web;

            }

        }

    }

}

To use it, simply create a new SPQuickSiteStructure object passing the site collection url and then do a standard loop through the SubWebs property starting from the SPQuickSiteStructure.RootWeb.

One thing to watch out for is the security. If you are going to show those sites to a user you’ll need to check whether the user is allowed to see the sites.

Thanks to Ishai for pointing out that the web urls are actually exposed by the public property Names, something that I missed initially. So the SPSite.AllWebs.Names gives you the site relative urls of all the webs but if you also want the web Titles and the web Guids you still need to go with the above code. Also the Names are returned as a flat list rather than a tree structure and you’ll need to build the structure yourself if you are not using the SPQuickSiteStructure class.

About these ads

8 Comments »

  1. [...] A Very Fast Method to Get the Site Collection’s Web Structure « SharePoint Internals – Hristo Pav… SAVE [...]

    Pingback by jthake's Bookmarks on Delicious — 21 October, 2008 @ 5:39 am

  2. [...] A Very Fast Method to Get the Site Collection’s Web Structure [...]

    Pingback by Links (10/23/2008) « Steve Pietrek - Everything SharePoint — 24 October, 2008 @ 12:42 am

  3. What a good piece of code! works like a charm………….many thanks

    Comment by Gcobani — 24 October, 2008 @ 10:41 am

  4. Would this be significantly different from using the portalsitemapprovider which is cached and security filtered to enumerate stuff under a site collection?

    Given, that the portalsitemapprovider does indeed hide hidden nodes, doesnt expose not-yet-published and publishing-expired information. Which may or may not be of interest to you and your users, ours at least require hidden to be hidden and not-published to be not visible in navigation (which is custom in our case, but based upon the portalsitemapprovider).

    Hmm. I have never tried to run the portalsitemapprovider against a site collection, perhaps it does not even support that. I shall have to give it a whirl.

    Comment by Myyz — 27 October, 2008 @ 9:59 am

  5. Would it be possible to expand this, so it works for publishing sites with all publishing pages in the Pages-list nested into that structure?

    Without opening memory consuming SPWebs of course.

    We have an ongoing case with Microsoft, because we used the PortalSiteMapProvider to generate our navigation. Unfortunately it accesses every SPWeb on it’s way, and consumes huge amount of memory while doing so. Microsoft don’t offer many alternatives and basicly tell us to either upgrade with more ram or pregenerate a static three-structure in xml to use.

    Comment by Peter — 28 October, 2008 @ 11:28 pm

  6. [...] I put the question out to some colleagues of mine, and Hristo Pavlov came up with an awesome solution that uses some unmanaged calls and reflection to get all the data you need. Check out his blog post at http://hristopavlov.wordpress.com/2008/10/20/a-very-fast-method-to-get-the-site-collections-web-stru…. [...]

    Pingback by Getting the structure of a SharePoint site without using SPWeb objects « Brian Farnhill — 11 February, 2009 @ 4:37 am

  7. hello Hristo,
    would like to know if i want to get some customize field, how can i do that?
    btw: where can i find the name “m_guidWebIds”?
    thanks a lot.

    Comment by Denny — 19 June, 2009 @ 5:34 am

  8. [...] A Very Fast Method to Get the Site Collection’s Web Structure [...]

    Pingback by Easiest way to find SharePoint Site Collection ID « ConfigLabs — 8 September, 2009 @ 12:47 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: