I have been tasked with moving an existing email system over to using Microsoft Graph. They are using .NET Framework 4.7.2. It seems the overwhelming majority of examples are for .NET 5 and up and this is causing some issues with my normal researching process.
I have the application sending emails successfully. I can send smaller attachments as part of the post without issue. The issue is with the large attachments. I can get it to upload the attachment. However, the email does not have the large attachment, despite having the message id in place on the post and a success status on the upload result.
The crazy thing is, I can get the small attachments to send something larger than 3MB, with 17MB being the largest I have attempted. All my research says that should not be possible so there is something I am missing.
I figure the large attachment issue is something I am doing wrong in the conversion from the .NET 5+ examples online and my .NET 4.7.2 client.
I have setup the permissions for Mail.ReadWrite and Mail.Send. Both are application type permissions.
To replicate:
My prototype program is a C# WinForms using .NET 4.7.2 with a single button. To eliminate as many assumptions as possible, I have brought all the code into one function, fired off with the button's click event.
I am using the following additional using statements.
using Microsoft.Graph;
using Microsoft.Graph.Models;
using System.IO;
using Azure.Identity;
Here is the function:
public async void SendEmailWithAttachments()
{
//Fill out tenantID, clientID, secret, objectID, path to a littleAttachment(<3MB) and bigAttachment(>3MB, <150MB)
//Add Email address as a string to EmailTo list.
string tenantID = "<insert tenant ID>";
string clientID = "<insert client ID>";
string secret = "<insert secret>";
string objectID = "<insert object id of the user and not the application>";
string EmailSubject = "Testing Email of Microsoft Graph Send Email with Attachments";
string EmailBody = "I really wish I could actually see the large attachments that I am uploading.";
List<string> EmailTo = new List<string>();
List<string> EmailCC = new List<string>();
List<string> EmailBCC = new List<string>();
FileInfo littleAttachment = new FileInfo(@"c:\example\smallfile.txt");
FileInfo bigAttachment = new FileInfo(@"c:\example\BigFile.jpg");
EmailTo.Add("[email protected]");
var cred = new ClientSecretCredential(tenantID, clientID, secret, new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud });
GraphServiceClient graphClient = new GraphServiceClient(cred);
var requestBody = new Microsoft.Graph.Users.Item.SendMail.SendMailPostRequestBody();
// Create message
var draftMessage = new Microsoft.Graph.Models.Message
{
Subject = "Large attachment"
};
var savedDraft = await graphClient.Users[objectID].Messages.PostAsync(draftMessage);
requestBody.Message = savedDraft;
//var result2 = await graphClient.Users[objectID].Messages[savedDraft.Id].GetAsync();
requestBody.Message.ToRecipients = new List<Recipient>();
requestBody.Message.CcRecipients = new List<Recipient>();
requestBody.Message.BccRecipients = new List<Recipient>();
requestBody.Message.Subject = EmailSubject;
requestBody.Message.Body = new ItemBody
{
ContentType = Microsoft.Graph.Models.BodyType.Text,
Content = EmailBody
};
foreach (string to in EmailTo)
{
requestBody.Message.ToRecipients.Add(new Recipient { EmailAddress = new EmailAddress { Address = to } });
}
foreach (string to in EmailCC)
{
requestBody.Message.CcRecipients.Add(new Recipient { EmailAddress = new EmailAddress { Address = to } });
}
foreach (string to in EmailBCC)
{
requestBody.Message.CcRecipients.Add(new Recipient { EmailAddress = new EmailAddress { Address = to } });
}
//---------------------------------------------------Big Attachments-------------------------------------------------------
var fileStream = System.IO.File.OpenRead(bigAttachment.FullName);
var uploadRequestBody = new Microsoft.Graph.Users.Item.Messages.Item.Attachments.CreateUploadSession.CreateUploadSessionPostRequestBody
{
AttachmentItem = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = bigAttachment.Name,
Size = fileStream.Length,
ContentType = "application/octet-stream"
}
};
var uploadSession = await graphClient.Users[objectID]
.Messages[requestBody.Message.Id]
.Attachments
.CreateUploadSession
.PostAsync(uploadRequestBody);
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, fileStream, maxSliceSize);
var totalLength = fileStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog =>
{
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
Console.WriteLine(uploadResult.UploadSucceeded ? "Upload complete" : "Upload failed");
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex.ToString()}");
}
//--------------------------------------------------------Big Attachments Ends---------------------------------------------
//-----------------------------------------------------Little Attachment Works---------------------------------------------
byte[] littleStream = File.ReadAllBytes(littleAttachment.FullName);
requestBody.Message.Attachments = new List<Attachment>
{
new Attachment
{
OdataType = "#microsoft.graph.fileAttachment",
Name = littleAttachment.Name,
AdditionalData = new Dictionary<string, object>
{
{
"contentBytes", Convert.ToBase64String(littleStream)
}
}
}
};
//-----------------------------------------------------Little Attachment End-----------------------------------------------
requestBody.SaveToSentItems = false;
await graphClient.Users[objectID].SendMail.PostAsync(requestBody);
}
I have tried to adapt answers from other solutions that were primarily in the beta/older library versions. It seems that with version 5 of the Microsoft Graph library, they put way too many breaking changes to actually adapt from those examples.
Though I was able to adapt the, save message as draft, save little attachments, save large attachments and send message, pattern.
I have gone to the Microsoft Graph site, which is the basis of my Frankenstein code. When they have C# available, it is only for what I assume to be .NET 5 and up. Generally, the changes have required a wrapper of some sort but they have largely worked except in this instance.
Nothing appears to be throwing an error. It just does not send. My understanding is it should be attaching when the attachment is uploaded since I am supplying the message id. I figure it is something simple that I am missing but I am at a loss.
The logic in the code you posted doesn't look correct basically what you should do is
eg
Users[objectID].Messages[draftMessage.Id].Send.PostAsync()
What your doing in SendMail
await graphClient.Users[objectID].SendMail.PostAsync(requestBody);
is just creating a new message from the properties you have in requestBody your posting, the id of the draft messages you created (which will be in the requestbody) is just ignored (you can test it yourself using the Graph explorer you can put junk in the id property and the endpoint will ignore it, should really throw an error IMO).
You also have
requestBody.SaveToSentItems = false;
This only works when you using the SendMail endpoint in a single operation where there is no draft message. So this won't work when you have large attachment and need to use the Send Endpoint, there is no other option so it will always save the Message to the SentItems folder and if you don't want it there you will need to delete it.
eg a simplified example
public static void SendEmailWithAttachments(GraphServiceClient graphClient, string objectID, string recipient,FileInfo littleAttachment, FileInfo bigAttachment)
{
// Create message
var draftMessage = new Microsoft.Graph.Models.Message
{
Subject = "Large attachment test",
};
draftMessage.ToRecipients = new List<Recipient>() { new Recipient() { EmailAddress = new EmailAddress() { Address = recipient } } };
//---------------------------------------------------Little Attachments-------------------------------------------------------
byte[] littleStream = File.ReadAllBytes(littleAttachment.FullName);
draftMessage.Attachments = new List<Attachment>
{
new Attachment
{
OdataType = "#microsoft.graph.fileAttachment",
Name = littleAttachment.Name,
AdditionalData = new Dictionary<string, object>
{
{
"contentBytes", Convert.ToBase64String(littleStream)
}
}
}
};
//---------------------------------------------------Little Attachments End---------------------------------------------------
var savedDraft = graphClient.Users[objectID].Messages.PostAsync(draftMessage).GetAwaiter().GetResult();
//---------------------------------------------------Big Attachments-------------------------------------------------------
var fileStream = System.IO.File.OpenRead(bigAttachment.FullName);
var uploadRequestBody = new Microsoft.Graph.Users.Item.Messages.Item.Attachments.CreateUploadSession.CreateUploadSessionPostRequestBody
{
AttachmentItem = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = bigAttachment.Name,
Size = fileStream.Length,
ContentType = "application/octet-stream"
}
};
var uploadSession = graphClient.Users[objectID]
.Messages[savedDraft.Id]
.Attachments
.CreateUploadSession
.PostAsync(uploadRequestBody).GetAwaiter().GetResult();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<FileAttachment>(uploadSession, fileStream, maxSliceSize);
var totalLength = fileStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog =>
{
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
try
{
// Upload the file
var uploadResult = fileUploadTask.UploadAsync(progress).GetAwaiter().GetResult();
Console.WriteLine(uploadResult.UploadSucceeded ? "Upload complete" : "Upload failed");
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex.ToString()}");
}
//--------------------------------------------------------Big Attachments Ends---------------------------------------------
//send the Draft
graphClient.Users[objectID].Messages[savedDraft.Id].Send.PostAsync().GetAwaiter().GetResult();
}