Get Library and Folder from document guid (C#)
-
30-09-2020 - |
Question
Information given
I am using version 16.0.0.0 of the Microsoft.SharePoint.Client library. There are several different document libraries in the SharePoint site. In code, we are given a list of document URLs (containing document GUIDs) and need to determine in which Document Library each is located, including the sub-folder if it is in a folder in the library.
Document Id (GUID) - gathered from document url query parameter "sourcedoc" ex: "http://mycompany.sharepoint.com/spsite/_layouts/15/WopiFrame.aspx?action=default&file=testfile.docx&sourcedoc={6A290A65-4759-41ED-A13E-3333B45DF133}"
Information needed
- Document Library Name
- Document Library URL
- Folder Name (if any)
- Folder URL (if any)
Current Code
using SP = Microsoft.SharePoint.Client;
public class LibraryAndFolderInfo
{
public LibraryAndFolderInfo();
public string FolderName { get; set; }
public string FolderUrl { get; set; }
public string LibraryName { get; set; }
public string LibraryBaseUrl { get; set; }
}
public class SPDataAccess
{
public LibraryAndFolderInfo GetLibraryAndFolderInfo(Guid documentGuid)
{
SP.File file = Web.GetFileById(documentGuid);
Context.Load(file);
if (file != null)
{
Context.Load(file.ListItemAllFields);
Context.ExecuteQuery();
SP.ListItem item = file.ListItemAllFields;
Context.Load(item.ParentList);
Context.ExecuteQuery();
SP.List list = item.ParentList;
Context.Load(list);
Context.ExecuteQuery();
Context.Load(item.Folder);
Context.ExecuteQuery();
SP.Folder folder = item.Folder;
Context.Load(folder);
Context.ExecuteQuery();
LibraryAndFolderInfo lib = new LibraryAndFolderInfo();
lib.LibraryName = list.Title;
lib.LibraryBaseUrl = list.DefaultViewUrl;
lib.FolderName = folder.Name;
lib.FolderUrl = folder.ServerRelativeUrl;
return lib;
}
return null;
}
protected SP.ClientContext Context { get; set; }
}
The code currently fails at this line: Context.Load(item.ParentList);
with the following error:
The property or field 'Title' has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.
at Microsoft.SharePoint.Client.ClientObject.CheckUninitializedProperty(String propName)
at Microsoft.SharePoint.Client.List.get_Title()
at MyNamespace.SPDataAccess.GetLibraryAndFolderInfo(Guid documentGuid) in c:\path\SPDataAccess.cs:line 27
This line seems to be attempting to retrieve the Title of the list as it is being loaded and failing. Is this a correct assumption?
I am not super familiar with how exactly to load properties, so everything after the failing line is my best guess as to how it would work.
What is this code supposed to look like? Has anyone else tried to get this information from a document?
Solution
Below are provided some corrections, fixes and considerations for your code:
1)The condition if (file != null)
is incorrect since it could not be used to determine whether file exists or not. The following example demonstrates how to verify whether file has been loaded:
SP.File file = Context.Web.GetFileById(documentGuid);
Context.Load(file);
Context.ExecuteQuery();
if (file.ServerObjectIsNull != null)
{
//File has been loaded..
}
2)item.Folder
does not return parent
Folder, it returns Folder
associated with List Item
and it is not the same
How to return parent
Folder for a File?
var file = Context.Web.GetFileById(documentGuid);
Context.Load(file,i => i.ListItemAllFields);
Context.ExecuteQuery();
var folder = Context.Web.GetFolderByServerRelativeUrl((string)file.ListItemAllFields["FileDirRef"]);
Context.Load(folder);
Context.ExecuteQuery();
3)List.DefaultViewUrl
returns a default view server relative url and it is not the same as List
server relative url
How to retrieve List Url?
var file = Context.Web.GetFileById(documentGuid);
var item = file.ListItemAllFields;
Context.Load(item.ParentList, l => l.RootFolder);
Context.ExecuteQuery();
var listUrl = item.ParentList.RootFolder.ServerRelativeUrl;
4)Since SharePoint CSOM supports Request Batching there is no need to submit multiple queries, instead in some cases you could submit only a single batch query as demonstrated below:
SP.File file = Context.Web.GetFileById(documentGuid);
SP.ListItem item = file.ListItemAllFields;
var list = item.ParentList;
Context.Load(list, l => l.Title, l => l.RootFolder);
Context.Load(item);
Context.ExecuteQuery(); //<- submit a single batch query
Fixed example
public class SPDataAccess
{
public SPDataAccess(SP.ClientContext ctx)
{
Context = ctx;
}
public LibraryAndFolderInfo GetLibraryAndFolderInfo(Guid documentGuid)
{
var file = Context.Web.GetFileById(documentGuid);
var item = file.ListItemAllFields;
var list = item.ParentList;
Context.Load(list, l => l.Title, l => l.RootFolder);
Context.Load(item);
Context.ExecuteQuery();
var info = new LibraryAndFolderInfo();
var folderUrl = (string)item["FileDirRef"];
info.LibraryName = list.Title; //list title
info.LibraryBaseUrl = list.RootFolder.ServerRelativeUrl; //list url
if (folderUrl.Replace(list.RootFolder.ServerRelativeUrl, string.Empty).Length > 0)
{
info.FolderName = folderUrl.Split('/').Last(); //folder name
info.FolderUrl = folderUrl;
}
return info;
}
protected SP.ClientContext Context { get; private set; }
}