Stephen McCormick
Stephen McCormick

Reputation: 1796

SharePoint Microsoft.SharePoint.Client.CamlQuery recursively return folders only (including subfolders)

I am attempting to pull back all the Folders and SubFolders (there can be any number) from a SharePoint site. I don't want the files (there could be thousands), so I am basically trying to just build a folder hierarchy. Additionally I only want the User created folders and the main "Documents" folders, not all the system ones.

That said, I found the following example that I though should have worked, but when I reduce it to just the folders I only get the top level folders:

https://stackoverflow.com/questions/16652288/sharepoint-client-get-all-folders-recursively

Here is the state of the current code. I am probably just missing something on the load (like an expresssion?):

    public static void LoadContent(Microsoft.SharePoint.Client.Web web, out Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.Folder>> listsFolders)
    {
        listsFolders = new Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.Folder>>();
        var listsItems = new Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.ListItem>>();

        var ctx = web.Context;
        var lists = ctx.LoadQuery(web.Lists.Where(l => l.BaseType == Microsoft.SharePoint.Client.BaseType.DocumentLibrary));
        ctx.ExecuteQuery();

        foreach (var list in lists)
        {
            var items = list.GetItems(Microsoft.SharePoint.Client.CamlQuery.CreateAllFoldersQuery());
            ctx.Load(items);
            listsItems[list.Title] = items;
        }
        ctx.ExecuteQuery();

        foreach (var listItems in listsItems)
        {
             listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == Microsoft.SharePoint.Client.FileSystemObjectType.Folder).Select(i => i.Folder);
        }
    }

UPDATE

Just to help out anyone else who might just want the main folders and subfolders as a list of urls, here is the final code. I suspect it could be simplified but it is working. The trick after the help below was to get the "root" folder paths, which required a separate query. I think that is where it could prove easier to just get Folders -> Subfolders, but I have Folders -> Subfolders -> Subfolders and this solution gets that last subfolder, along with the root folders.

    public static void LoadContent(Microsoft.SharePoint.Client.Web web, List<String> foldersList)
    {
        Dictionary<string, IEnumerable<Folder>>  listsFolders = new Dictionary<string, IEnumerable<Folder>>();
        var listsItems = new Dictionary<string, IEnumerable<ListItem>>();

        var ctx = web.Context;
        var lists = ctx.LoadQuery(web.Lists.Include(l => l.Title).Where(l => l.BaseType == BaseType.DocumentLibrary && !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));
        ctx.ExecuteQuery();

        foreach (var list in lists)
        {
            ctx.Load(list.RootFolder);
            ctx.ExecuteQuery();
        }

        foreach (var list in lists)
        {
            if (list.Title != "Form Templates" && list.Title != "MicroFeed" && list.Title != "Site Assets" && list.Title != "Site Pages")
            {
                foldersList.Add(list.RootFolder.ServerRelativeUrl);

                var items = list.GetItems(CamlQuery.CreateAllFoldersQuery());
                ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));
                listsItems[list.Title] = items;
            }
        }
        ctx.ExecuteQuery();

        foreach (var listItems in listsItems)
        {
            listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == FileSystemObjectType.Folder).Select(i => i.Folder);
        }

        foreach (var item in listsFolders)
        {
            IEnumerable<Folder> folders = item.Value;
            foreach (Folder folder in folders)
            {
                foldersList.Add(folder.ServerRelativeUrl);
            }
        }
    }

An example of what this returns:

enter image description here

Upvotes: 1

Views: 747

Answers (2)

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59358

1) In the provided example, to return Folder object, it needs to be explicitly included otherwise the exception occur, so replace the line:

ctx.Load(items);

with:

ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));

2) "system" libraries could be excluded like this:

var lists = ctx.LoadQuery(web.Lists.Where(l => !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));

Modified example

public static void LoadContent(Web web, out Dictionary<string, IEnumerable<Folder>> listsFolders)
{
        listsFolders = new Dictionary<string, IEnumerable<Folder>>();
        var listsItems = new Dictionary<string, IEnumerable<ListItem>>();

        var ctx = web.Context;
        var lists = ctx.LoadQuery(web.Lists.Include(l =>l.Title).Where(l => l.BaseType == BaseType.DocumentLibrary && !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));
        ctx.ExecuteQuery();

        foreach (var list in lists)
        {
            var items = list.GetItems(CamlQuery.CreateAllFoldersQuery());
            ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));
            listsItems[list.Title] = items;
        }
        ctx.ExecuteQuery();

        foreach (var listItems in listsItems)
        {
            listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == FileSystemObjectType.Folder).Select(i => i.Folder);
        }
}

Upvotes: 1

Lee
Lee

Reputation: 5493

Try this.

var lists = ctx.LoadQuery(ctx.Web.Lists.Where(l => l.BaseType == BaseType.DocumentLibrary));                
                ctx.ExecuteQuery();
                foreach (var list in lists)
                {
                    Console.WriteLine(list.Title);
                    ListItemCollection listitems = list.GetItems(CamlQuery.CreateAllFoldersQuery());
                    ctx.Load(listitems, items => items.Include(item => item.Id,item=>item.Folder));
                    ctx.ExecuteQuery();
                    foreach (var item in listitems)
                    {
                        Console.WriteLine(item.Folder.ServerRelativeUrl);
                    }                                        
                }

Upvotes: 0

Related Questions