When working with SharePoint Document Libraries, you often need to target specific files and subfolders within a nested directory structure. While retrieving all items from a list is straightforward, filtering by a specific folder path using the Client-Side Object Model (CSOM) and CAML (Collaborative Application Markup Language) requires a nuanced understanding of query scopes and internal field references.
In this guide, you will learn the most effective methods to retrieve items from a specific folder, handle large datasets with pagination, and understand the critical difference between query scopes.
Understanding the FileDirRef Property
The most direct way to filter by a folder in a CAML query is by using the FileDirRef internal field. This field represents the server-relative URL of the folder containing the item. When you want to find everything inside a specific path, you compare this field against the directory string.
However, a common mistake is neglecting the Scope attribute in the <View> element. By default, a CAML query only looks at the root level of the list. To look inside folders, you must explicitly define the scope.
Using the RecursiveAll Scope
To retrieve both files and subfolders within a directory, you should use the RecursiveAll scope. If you only need files, you can use Recursive.
Here is how you can structure your CAML query to target a specific folder path:
List DocumentsList = clientContext.Web.Lists.GetByTitle("YourLibraryName");
CamlQuery camlQuery = new CamlQuery();
// Use RecursiveAll to include both files and folders
camlQuery.ViewXml = @"<View Scope='RecursiveAll'>
<Query>
<Where>
<Eq>
<FieldRef Name='FileDirRef'/>
<Value Type='Text'>/sites/your-site/Library/TargetFolder</Value>
</Eq>
</Where>
</Query>
<RowLimit Paged='TRUE'> 30 </RowLimit>
</View>";
ListItemCollection listItems = DocumentsList.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
In this example, the FileDirRef must match the server-relative path exactly. If you aren't sure of the exact path, you can use the <Contains> operator instead of <Eq>, though this may have performance implications on very large libraries.
The Recommended Approach: FolderServerRelativeUrl
While filtering via CAML XML works, the SharePoint CSOM provides a cleaner, more robust property on the CamlQuery object itself: FolderServerRelativeUrl.
When you set this property, SharePoint automatically restricts the context of the query to that specific folder and its descendants. This is often more reliable than manually stringing together the FileDirRef in the XML.
CamlQuery camlQuery = new CamlQuery();
camlQuery.FolderServerRelativeUrl = "/sites/your-site/Library/TargetFolder";
camlQuery.ViewXml = "<View Scope='RecursiveAll'><Query></Query></View>";
ListItemCollection listItems = DocumentsList.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
By using FolderServerRelativeUrl, you simplify your CAML XML and reduce the risk of path-formatting errors.
Handling Large Libraries with Pagination
If the folder you are querying contains thousands of items, you will likely hit the SharePoint List View Threshold (typically 5,000 items). To handle this, you must implement pagination using the ListItemCollectionPosition property.
This approach ensures your application remains performant and avoids timeout errors:
private async Task<List<ListItem>> GetAllItemsInFolder(ClientContext clientContext, List list, string folderPath)
{
var result = new List<ListItem>();
ListItemCollectionPosition itemPosition = null;
while (true)
{
CamlQuery camlQuery = new CamlQuery();
camlQuery.ListItemCollectionPosition = itemPosition;
camlQuery.FolderServerRelativeUrl = folderPath;
camlQuery.ViewXml = @"<View Scope='RecursiveAll'>
<ViewFields>
<FieldRef Name='Title'/>
<FieldRef Name='FileRef'/>
</ViewFields>
<RowLimit>5000</RowLimit>
</View>";
ListItemCollection listItems = list.GetItems(camlQuery);
clientContext.Load(listItems);
await clientContext.ExecuteQueryAsync();
itemPosition = listItems.ListItemCollectionPosition;
foreach (ListItem item in listItems)
{
result.Add(item);
}
if (itemPosition == null)
{
break;
}
}
return result;
}
Alternative: Manual Folder Traversal
In some scenarios, you might want to explore folders programmatically rather than querying them. This is useful for building a tree-view UI or when you need to perform specific logic at each folder level. You can load the Folders collection directly from the RootFolder or any specific Folder object.
clientContext.Load(DocumentsList.RootFolder.Folders);
clientContext.ExecuteQuery();
foreach (Folder subFolder in DocumentsList.RootFolder.Folders)
{
clientContext.Load(subFolder.Files);
clientContext.ExecuteQuery();
// Process files in each folder
}
While this is less efficient for simply "getting items," it provides maximum control over the traversal process.
Frequently Asked Questions
What is the difference between Recursive and RecursiveAll?
Recursive retrieves only the files within the specified folder and its subfolders. RecursiveAll retrieves both the files and the folder objects themselves. If you need to manipulate the subfolders in your code, always use RecursiveAll.
Why is my CAML query returning items from the root instead of the folder?
This usually happens because the Scope='RecursiveAll' attribute is missing from the <View> tag. Without this attribute, SharePoint ignores the folder hierarchy and searches the root level. Additionally, ensure your FileDirRef path does not have a trailing slash unless the library structure requires it.
Can I use QueryOptions to specify a folder?
In older SharePoint Web Services (SOAP API), <QueryOptions> was used to define the folder path. In modern CSOM development, it is best practice to use the FolderServerRelativeUrl property on the CamlQuery object or filter by FileDirRef in the XML.
Wrapping Up
Retrieving items from a specific folder in SharePoint requires a combination of the right CAML scope and accurate path filtering. For most modern applications, using the FolderServerRelativeUrl property on the CamlQuery object is the cleanest and most efficient method. However, if you need complex filtering, the FileDirRef field remains a powerful tool in your CAML arsenal.
Always remember to implement pagination when dealing with large libraries to ensure your solution scales effectively as your data grows.