2

I'm writing a utility in C# using the Microsoft.Graph SDK that connects and reads a user's OneDrive. I have created an App Registration, granted the application Files.Read.All permissions, and given Admin consent per the documentation.

enter image description here

I am able to connect to the Graph and the metadata for the OneDrive:

List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");

var authenticationProvider = new MsalAuthenticationProvider(cca, scopes.ToArray());
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);

var drive = await graphClient.Users[userId].Drive
    .Request()
    .GetAsync();

It appears to correctly connect to the OneDrive, as evidenced by the properties that do return correct data like Quota, Owner, etc.

The issue is that the Items object is null, so I can't read any of the drive items:

enter image description here

I tried using the returned drive Id to access the drive directly, but received the same result:

var driveById = await graphClient.Drives[drive.Id]
    .Request()
    .GetAsync();

The few examples I found don't indicate any additional Request options or missing permissions. So how do I access the OneDrive Items?

Joel Cochran
  • 7,139
  • 2
  • 30
  • 43
  • 1
    Don't have the SDK installed to test, but try something like `graphClient .Drives[drive.Id].Root.Children.Request()...` – Joachim Isaksson Jun 29 '21 at 18:09
  • That seems to work as it returns a DriveItemChildrenCollectionPage object that has contents. Thanks! – Joel Cochran Jun 29 '21 at 18:20
  • 1
    I delete my previous comment because I made a stupid mistake. The code is working great as documented, apologies to the SDK team for my frustration. – Joel Cochran Jun 29 '21 at 20:21
  • 1
    @JoachimIsaksson - if you add it as an answer I'll accept it. If not, I'll write it up for anyone who finds this in the future. – Joel Cochran Jun 29 '21 at 20:27
  • All good @JoelCochran. Thank you for writing a clean StackOverflow post! Might as well write up the answer to your question to complete the Q&A. – Michael Mainer Jun 30 '21 at 23:31

1 Answers1

3

The solution for this issue was presented in the comments, so I'm writing it up here for completeness.

ORIGINAL CODE:

var rootDrive = await GraphClient.Users[UserId].Drive.Request().GetAsync();

This returns the metadata of the user's OneDrive, but does not capture the Children. We will need this information later, however, so the final solution uses both this reference and the updated code.

UPDATED CODE:

To do that, you need to reference the drive's Root and its Children:

var driveItems = await GraphClient.Users[UserId].Drive
                                                .Root
                                                .Children
                                                .Request()
                                                .GetAsync();

Doing so returns an IDriveItemChildrenCollectionPage:

enter image description here

PROCESS THE CHILDREN:

For small samples, a standard foreach will work fine, but for larger collections you will need to implement a PageIterator (which I have not done yet). To get the children of this driveItem, you will need the drive Id of the root element as well as the current driveItem.Id:

var children = await GraphClient.Drives[rootDriveId].Items[item.Id].Children.Request().GetAsync()

Putting it altogether, it looks something like this:

public async Task ListOneDrive()
{
    var rootDrive = await GraphClient.Users[UserId].Drive.Request().GetAsync();

    var driveItems = await GraphClient.Users[UserId].Drive
                                                    .Root
                                                    .Children
                                                    .Request()
                                                    .GetAsync();

    foreach (var item in driveItems)
    {
        await ListDriveItem(rootDrive.Id, item);
    }
}

public async Task ListDriveItem(string rootDriveId, DriveItem item, int indentLevel = 1)
{
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < indentLevel; i++)
    {
        sb.Append($"  ");
    }

    if (item.Folder != null)
    {
        Console.WriteLine($"{sb.ToString()}> {item.Name}/");

        var children = await GraphClient.Drives[rootDriveId].Items[item.Id].Children.Request().GetAsync();
        foreach (var child in children)
        {
            await (ListDriveItem(rootDriveId, child, indentLevel + 1));
        }
    }
    else if (item.File != null)
    {
        Console.WriteLine($"{sb.ToString()}  {item.Name}");
    }
}

This example is from a Console app that uses a recursive call to drill down through all the folders. Both methods really should have a PageIterator as mentioned above.

Update as of Microsoft.Graph 5.12.0

    var driveItem = await graphClient.Me.Drive.GetAsync();
    var driveItems = await graphClient.Drives[driveItem.Id].Items["root"].Children.GetAsync();

    foreach (var item in driveItems.Value)
    {
        await ListDriveItem(driveItem.Id, item);
    }

and then for ListDriveItem function:

   public async Task ListDriveItem(string rootDriveId, DriveItem item, int indentLevel = 1)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < indentLevel; i++)
        {
            sb.Append($"  ");
        }

        if (item.Folder != null)
        {
            Console.WriteLine($"{sb.ToString()}> {item.Name}/");

            var children = await graphClient.Drives[rootDriveId].Items[item.Id].Children.GetAsync();
            foreach (var child in children.Value)
            {
                await (ListDriveItem(rootDriveId, child, indentLevel + 1));
            }
        }
        else if (item.FileObject != null)
        {
            Console.WriteLine($"{sb.ToString()}  {item.Name}");
        }
    }
RonnieScotland
  • 132
  • 2
  • 5
Joel Cochran
  • 7,139
  • 2
  • 30
  • 43
  • I get the error "'RootRequestBuilder' does not contain a definition for 'Children' and no accessible extension method 'Children' accepting a first argument of type 'RootRequestBuilder' could be found (are you missing a using directive or an assembly reference?) for the line: await graphClient.Drives[driveId].Root.Children.Request().GetAsync() – Rye bread May 23 '23 at 12:55
  • Were you able to resolve this issue? This code was from a couple years ago, and came from Microsoft.Graph v3.35. The current version is 5.12, so it is possible the Graph SDK has changed since this code was written. – Joel Cochran May 30 '23 at 14:50
  • 1
    Added an update for Microsoft.Graph 5.12.0 – RonnieScotland May 31 '23 at 02:09