Compartilhar via


SPFolderHierarchy Class

This class is an abstraction over the folders in a SharePoint Server list designed to work around limitations in the SharePoint Server object model around iterating the folders in a large list.

Inheritance Hierarchy

System.Object
  Microsoft.Office.Server.Utilities.SPFolderHierarchy

Namespace:  Microsoft.Office.Server.Utilities
Assembly:  Microsoft.Office.Server (in Microsoft.Office.Server.dll)

Syntax

'Declaration
Public NotInheritable Class SPFolderHierarchy
'Usage
Dim instance As SPFolderHierarchy
public sealed class SPFolderHierarchy

Remarks

The list's root folder (SPList.RootFolder) is not contained inthis collection.This abstraction trades off memory to be able to avoid the expensive queries required to navigate a list's folder hierarchy using the SPFolder.SubFolders API. This class exposes the list's folders as a hierarchy (vs. a flat unordered enumeration of folders), which would be the most efficient in terms of both SQL IO and web front end memory consumption, but would not have allowed getting parent-child and sort order that matches conventional access patterns. The SPFolderHierarchy class attempts to retrieve the folder list items via a query (<see cref="T:Microsoft.SharePoint.SPQuery"/>) which will succeed if:

  1. The list is not large. See MaxItemsPerThrottledOperation.

  2. The content type field on the list is indexed. See ListHasIndexedContentType(SPList).

  3. There are fewer than SPWebApplication.MaxItemsPerThrottledOperation folders in the list (including document sets and any other folder-derived content type)

If none of those conditions are true, then the query for the folder items will be throttled. In that case, the SPFolderHierarchy falls back and tries to get the top level of folders (i.e., direct children of list.RootFolder) by using the SPFolder.SubFolders API and then each time the SPFolderHierarchy.GetSubFolders(System.String) method or the SPFolderHierarchy.GetSubFolders(System.String,System.Boolean) method is called, another call to the SPFolder.SubFolders API is made. The query in this call is throttled as soon as a folder that contains more items than the SPWebApplication.MaxItemsPerThrottledOperation property is set to allow either by counting direct decendents or based on the entire subtree depending on whether recursive subfolders were requested. Requesting recursive subfolders will make these queries fail much sooner as a list grows past the large list threshhold. Once the fall back logic without requesting recursive subfolders described above is also throttled, there is no way to obtain the folder hierarchy of the list without iterating over every item in the list and pulling out the folders. This is not recommended since it is very expensive to iterate over all items in a large list.

Instead, consider either:

  1. Indexing the content type field on the list. To learn more, see the EnsureContentTypeIndexedIfLargeList(SPList) method, which will allow the SPFolderHierarchy object to successfully use queries get the folder hierarchy in an efficient manner.

  2. Create a content organizer rule for the list to auto folder based on count. If you set the auto-folder count so that the root folder items plus subfolder count will remain less than the SPWebApplication.MaxItemsPerThrottledOperation count until the list has several million items, you should be able to accommodate cases where it is not possible to index the content type field. For example, if SPWebApplication.MaxItemsPerThrottledOperation is 5000 and the auto-folder count is set to 2000, then SharePoint Server can get to approximately 6002000 before throttling on SPFolder.SubFolders is throttled (2000 items plus 3000 subfolders in the root folder of the list). SharePoint Server can support lists that are larger. If the list is expected to grow beyond two million items, more planning will be required.

Examples

using System;

using Microsoft.SharePoint;
using Microsoft.Office.Server.Utilities;
using System.Collections.Generic;
using System.Globalization;

namespace Microsoft.SDK.SharePointServer.Samples
{
    public static class FolderHierarchyCodeSample1
    {
        private static void ProcessFolder(SPFolder folder)
        {
            // Put your code here
        }

        private static void HandleThrottledException(SPException ex)
        {
            // This means the folder hierarchy could not be retrieved.  This happens if all of the following are true:
            // 1. the list does not have an index on the content type ID field (see ContentIterator.ListHasIndexedContentType(SPList))
            // 2. the list was large enough that the folder items could not be queried directly
            // 3. at least one folder in the hierarchy had enough subfolders that SPFolder.SubFolders was also throttled
            //
            // If this happens, there is no way get the folder hierarchy and the SPBuiltInFieldId.ContentTypeId field must
            // be indexed.
        }

        private static void ProcessFolderRecursively(SPFolderHierarchy folders, SPFolder folder)
        {
            ProcessFolder(folder);

            SPFolderHierarchy subFolders = folders.GetSubFolders(folder.ServerRelativeUrl);
            foreach (SPFolder subfolder in ((IEnumerable<SPFolder>)subFolders))
            {
                ProcessFolderRecursively(folders, subfolder);
            }
        }

        public static void ProcessAllFoldersInList(SPList list)
        {
            string viewFields = ContentIterator.FolderCoreViewFields +
                "<FieldRef Name=\"ItemChildCount\"/>" +
                "<FieldRef Name=\"FolderChildCount\"/>";

            // Handle the root web separately since it is not contained in the hierarchy
            ProcessFolder(list.RootFolder);

            // Check if the list has folders other than the root folder
            if (!SPFolderHierarchy.ListHasFolders(list))
                return;

            try
            {
                SPFolderHierarchy folders = new SPFolderHierarchy(list, true, viewFields, true);

                Console.WriteLine(string.Format(CultureInfo.InvariantCulture, 
                    "Total # of folders in list:  {0}",
                    folders.Count));

                SPFolderHierarchy subFolders = folders.GetSubFolders(list.RootFolder.ServerRelativeUrl);
                foreach (SPFolder subfolder in ((IEnumerable<SPFolder>)subFolders))
                {
                    ProcessFolderRecursively(folders, subfolder);
                }
            }
            catch (SPQueryThrottledException ex)
            {
                HandleThrottledException(ex);
            }
        }
    }
}

using System;

using Microsoft.SharePoint;
using Microsoft.Office.Server.Utilities;
using System.IO;

namespace Microsoft.SDK.SharePointServer.Samples
{
    class FolderHierarchyCodeSample2
    {
        // Replace with your folder name
        private static string templateFolder = "AdventureWorksTemplates";

        private static void ProcessFolder(SPFolder folder)
        {
            // Put your code here
        }

        public static void ProcessFoldersFromList(SPList list, string[] folderUrls)
        {
            // Create a folder hierarchy instead of making one query per folder
            SPFolderHierarchy folders = new SPFolderHierarchy(list, true);

            foreach (string folderUrl in folderUrls)
            {
                SPFolder folder;

                if (folders.TryGetFolder(folderUrl, out folder))
                {
                    if (folder.Exists)
                    {
                        ProcessFolder(folder);
                    }
                }
            }
        }

        public static void ProcessFoldersFromWeb(SPWeb web, string[] folderUrls)
        {
            // Since folderUrls may span many lists, do not create folder hierarchies
            foreach (string folderUrl in folderUrls)
            {
                SPFolder folder;

                // Try to get the folder and make sure it exists (this does not create the folder if it does not)
                if (SPFolderHierarchy.TryGetFolderByUrl(web, folderUrl, true, out folder))
                {
                    ProcessFolder(folder);
                }
            }
        }

        public static SPFile AddTemplateResource(SPSite site, Stream fileStream, string fileName)
        {
            // Get (and create if it does not exist) a folder off of site.RootWeb.RootFolder to use to store resource files
            SPFolder resourceFolder = SPFolderHierarchy.GetSiteCollectionResourceFolder(site, templateFolder, true);

            // Create the file or update it if it already exists
            return resourceFolder.Files.Add(fileName, fileStream, true);
        }
    }
}

Thread Safety

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

See Also

Reference

SPFolderHierarchy Members

Microsoft.Office.Server.Utilities Namespace