1

I have the following code:

var graphServiceClient = GraphClientFactory.GetGraphServiceClient(config.ClientId, config.Authority, config.Scopes);

MailMessagePage = await graphServiceClient.Me.MailFolders.Inbox.Messages
    .Request()
        .Expand("attachments")
    .GetAsync();

foreach (var mm in MailMessagePage)
{
    foreach (var a in mm.Attachments)
    {

    }
}

This code is successfully downloading the Inbox Messages and the Inner foreach loop is enumerating through the attachments collection. Here is a example:

Watch of a

What is not included is the actual attachment data. Does anyone have an example of downloading the actual attachment data?

Thanks

Based on the suggestions from Darrel I implemented the following.

var outlookItem = await builder.Request().GetAsync();

Is returning the Metadata for the attachment bu not the attachment itself. I am after the data.

MailMessagePage = await graphServiceClient.Me.MailFolders.Inbox.Messages
    .Request()
    .Expand("attachments")
    .GetAsync();

foreach (var mm in MailMessagePage)
{
    foreach (var itemAttachment in mm.Attachments)
    {
        if(itemAttachment is ItemAttachment)
        {
            var builder = new ItemAttachmentRequestBuilder(graphServiceClient.Me.Messages[mm.Id].Attachments[itemAttachment.Id].RequestUrl, graphServiceClient);
            var outlookItem = await builder.Request().GetAsync(); 
        }
    }
}

Watch showing itemAttachnment vs outlookItem

2 Answers2

2

Have you tried the below? Taken from here.

var attachments = await graphServiceClient.Me.Messages[messageId]
   .Attachments
   .Request()
   .GetAsync();

You can then check if they're a FileAttachment which has a ContentBytes property that contains the actual attachment data.

Shoejep
  • 4,414
  • 4
  • 22
  • 26
  • I used .Expand("attachments") which returns the same information as your suggested code returns. The contentType for the attachments I have is: message/rfc822. This is simply an attached Rfc822 email message. There is no ContentBytes Property. – William Holmes Feb 20 '20 at 17:30
  • You have to cast to the FileAttachment class to get ContentBytes property but by looking at your image, it's a different class anyway. I would suggest to take a look at this [SO question and answer](https://stackoverflow.com/questions/42210430/microsoft-azure-graph-api-download-itemattachment-content/55382827#55382827) which appears to be similar. – Shoejep Feb 20 '20 at 17:46
1

The challenge here is that Attachment is actually an abstract type and there are multiple different concrete types. FileAttachment has a ContentBytes property, but ItemAttachment has a navigation property Item that points to an OutlookItem. This means that you need to do a separate request to retrieve the OutlookItem. There doesn't appear to be a request builder for that particular Item.

            if (attachment is ItemAttachment)
            {
                var requestUrl = graphClient.Me.Messages[message.Id].Attachments[attachment.Id].RequestUrl + "/item"; 
                var request = new HttpRequestMessage() {
                    RequestUri = new Uri(requestUrl)
                };
                var outlookItemResponse = graphClient.HttpProvider.SendAsync(request);
               var outlookItem = new ResponseHandler(new Serializer()).HandleResponse<OutlookItem>(outlookItemResponse);
            }

We acknowledge that this is not intuitive. We will be investigating how we can make accessing these derived types easier.

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • Thank you for this insight. I am not finding the appropriate name space for the ItemAttachmentRequestBuilder? Can you point me to the appropriate reference? – William Holmes Feb 21 '20 at 16:20
  • That's strange. It lives in the same Microsoft.Graph namespace https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/src/Microsoft.Graph/Requests/Generated/ItemAttachmentRequestBuilder.cs – Darrel Miller Feb 21 '20 at 16:39
  • When using the first example the itemAttachmentRequestBuilder always returns null. I edited my original post to show my implementation. I'm sure I am just missing something. – William Holmes Feb 22 '20 at 14:24
  • @WilliamHolmes You are correct. It is only responses that are derived types that are correctly deserialized into their specialized types using the returned OData.Type annotation in the response. With RequestBuilders we can't predict what type is going to come back. You would need to use the second method. In the future we may consider creating extension methods to models to use a model as the root for creating a request. – Darrel Miller Feb 22 '20 at 17:35
  • I have implemented the second method. the var outlookItem = await builder.Request().GetAsync(); returns the attachment Metadata but not the attachment content itself. This call essentially returns same information as was already in mm.attachments. Is there an additional step to return the content. Note this attachment is of type: message/rfc822 does this require some special handling? – William Holmes Feb 24 '20 at 13:57
  • @WilliamHolmes outlookItem has a Item navigation property which retrieves the actual OutlookItem. It appears that that specific OutlookItem is marked as contains target. So, you need the url /me/messages/{id}/attachments/{id}/item to actually retrieve the OutlookItem. I have not found a corresponding builder for that, so you probably will have to break out of the SDK to make that work. – Darrel Miller Feb 24 '20 at 21:02