A method to get all child files and folders #1211
-
I am attempting to get all files and folders for a given folder that gets passed in. My goal is to iterate over the returned list items, and update the metadata fields ( How can I update this method so it returns the files and folders (currently returns empty collection) and also load the relevant fields for updating metadata on each file and folder? public async Task<IEnumerable<IListItem>> GetChildFilesAndFolders(IFolder folder)
{
var list = await _sharePointContext.Web.Lists.GetByServerRelativeUrlAsync(folder.ServerRelativeUrl,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind, p => p.TypeAsString,
p => p.Title));
var camlQuery = new CamlQueryOptions
{
FolderServerRelativeUrl = folder.ServerRelativeUrl,
ViewXml = $@"
<View Scope=""RecursiveAll"">
<Query>
<Or>
<Eq>
<FieldRef Name=""FSObjType"" />
<Value Type=""Integer"">0</Value>
</Eq>
<Eq>
<FieldRef Name=""FSObjType"" />
<Value Type=""Integer"">1</Value>
</Eq>
</Or>
</Query>
</View>"
};
await list.LoadItemsByCamlQueryAsync(camlQuery,
p => p.Title, p => p.FileSystemObjectType,
p => p.Folder.QueryProperties(f => f.Name, f => f.ListItemAllFields.QueryProperties(li => li.All,
li => li.ParentList.QueryProperties(p => p.Title,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind,
p => p.TypeAsString, p => p.Title)))),
p => p.File.QueryProperties(f => f.Name, f => f.ListItemAllFields.QueryProperties(li => li.All,
li => li.ParentList.QueryProperties(p => p.Title,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind,
p => p.TypeAsString, p => p.Title)))));
return list.Items.AsRequested();
} |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 13 replies
-
I was able to find a request. It was made when getting the list. Very strange the way this SDK works. Request:
Response:
|
Beta Was this translation helpful? Give feedback.
-
@adamfisher : you need to specify the fields you need in the CAML query, see https://pnp.github.io/pnpcore/using-the-sdk/listitems-intro.html#c-getting-list-items-via-the-loaditemsbycamlquery-approach for some samples. Also note that there's a limitation when folder names contain # or & characters, best to filter via |
Beta Was this translation helpful? Give feedback.
-
@adamfisher : The "Guid should contain 32 digits with 4 dashes" error suggests your pushing in a non guid somewhere were a guid is expected. The error comes the server call response. I would suggest to test field by field to find the one that's wrong. What also makes sense is to request the fields you want to set as part of your CAML query, this way you can inspect the current value of the field you want to set. The last approach you've put here is not needed as you're loading items twice, once via CAML, once via the regular item enumeration. |
Beta Was this translation helpful? Give feedback.
-
My final methods (working): public static async Task<IEnumerable<IListItem>> GetChildFilesAndFoldersAsync(this IFolder folder)
{
var list = await folder.PnPContext.Web.Lists.GetByServerRelativeUrlAsync(folder.ServerRelativeUrl,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind, p => p.TypeAsString,
p => p.Title));
var camlQuery = new CamlQueryOptions
{
FolderServerRelativeUrl = folder.ServerRelativeUrl,
// FSObjType = 0 (folder) // FSObjType = 1 (file)
ViewXml = @"
<View Scope=""RecursiveAll"">
<ViewFields>
<FieldRef Name='Title' />
<FieldRef Name='FileLeafRef' />
<FieldRef Name='FileRef' />
<FieldRef Name='FileDirRef' />
<FieldRef Name='ParentList' />
</ViewFields>
<Query>
<Or>
<Eq><FieldRef Name=""FSObjType"" /><Value Type=""Integer"">0</Value></Eq>
<Eq><FieldRef Name=""FSObjType"" /><Value Type=""Integer"">1</Value></Eq>
</Or>
</Query>
</View>"
};
await list.LoadItemsByCamlQueryAsync(camlQuery);
return list.Items.AsRequested();
} Followed by: private async Task SetFieldMetaData(IFolder targetFolder, MyData myData)
{
var columnValues = await GetColumnValuesAsync(targetFolder, myData);
var items = (await targetFolder.GetChildFilesAndFoldersAsync()).ToList();
items.Add(targetFolder.ListItemAllFields);
_logger.LogInformation("Setting metadata for {ItemCount} items in \"{FolderName}\"...", items.Count, targetFolder.Name);
foreach (var item in items)
{
var isFolder = await item.IsFolderAsync();
var listItem = isFolder ? item.Folder.ListItemAllFields : item.File.ListItemAllFields;
await listItem.EnsurePropertiesAsync(li => li.All,
li => li.UniqueId,
li => li.Title,
li => li.File.QueryProperties(p => p.All,
f => f.UniqueId,
f => f.Name,
f => f.ListItemAllFields.QueryProperties(li => li.All,
li => li.ParentList.QueryProperties(p => p.Title,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind,
p => p.TypeAsString, p => p.Title)))),
li => li.Folder.QueryProperties(p => p.All,
f => f.UniqueId,
f => f.Name,
f => f.ListItemAllFields.QueryProperties(li => li.All,
li => li.ParentList.QueryProperties(p => p.Title,
p => p.Fields.QueryProperties(p => p.InternalName, p => p.FieldTypeKind,
p => p.TypeAsString, p => p.Title)))));
foreach (var columnValue in columnValues)
{
listItem[columnValue.FieldInternalName] = columnValue.DefaultValue;
}
await listItem.UpdateAsync();
}
} |
Beta Was this translation helpful? Give feedback.
I've fixed #1213 , so you can use those methods as well from the next nightly. Looking at your repro code you seem to be handling the metadata updates via the respective IFile and IFolder ListItemAllFields collection. As you're anyhow doing a query to first get these list items it's more efficient to immediately update them: