msgraph-sdk-dotnet icon indicating copy to clipboard operation
msgraph-sdk-dotnet copied to clipboard

Unable to Read Bulk Emails with Expand Attachments filters Using Microsoft Graph API's

Open v2mounish opened this issue 1 year ago • 10 comments

Description:

we are unable to Read bulk emails and we are getting the error below.

Reference Document Links:

https://learn.microsoft.com/en-us/training/modules/optimize-network-traffic/

Installed Packages are:

NuGet\Install-Package Microsoft.Identity.Client -Version 4.49.1 NuGet\Install-Package Microsoft.Graph -Version 4.50.0 NuGet\Install-Package Microsoft.Extensions.Configuration -Version 7.0.0 NuGet\Install-Package Microsoft.Extensions.Configuration.FileExtensions -Version 7.0.0 NuGet\Install-Package Microsoft.Extensions.Configuration.Json -Version 7.0.0 Error Message :

RailySend Email Failed. Send Email Failed One or more errors occurred.  Status Code: NotFound Microsoft.Graph.ServiceException: Code: ErrorItemNotFound Message: The specified object was not found in the store., Cannot create a message. Inner error:       AdditionalData:       date: 2023-01-17T13:46:39       request-id: 69a0f44c-ff5e-49b7-a618-048261f0c47a       client-request-id: 69a0f44c-ff5e-49b7-a618-048261f0c47a ClientRequestId: 69a0f44c-ff5e-49b7-a618-048261f0c47a

at Microsoft.Graph.HttpProvider.d__18.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Graph.BaseRequest.d__40.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Graph.BaseRequest.d__33.MoveNext()

v2mounish avatar Mar 28 '23 11:03 v2mounish

Thanks for raising this @v2mounish

Any chance you can share an updated stacktrace of the error you are getting? As well as a code sample of where the exception is thrown?

andrueastman avatar Mar 29 '23 08:03 andrueastman

Source Code :

var inboxMessages = await graphClient .Users[emailRequest.ClientUserName] .MailFolders[mailFolder[a].DisplayName] .Messages .Request() //.Expand("attachments") .Top(1000).OrderBy("receivedDateTime asc") .GetAsync();

Error Message : RailySend Email Failed. Send Email Failed One or more errors occurred.  Status Code: NotFound Microsoft.Graph.ServiceException: Code: ErrorItemNotFound Message: The specified object was not found in the store., Cannot create a message. Inner error:       AdditionalData:       date: 2023-01-17T13:46:39       request-id: 69a0f44c-ff5e-49b7-a618-048261f0c47a       client-request-id: 69a0f44c-ff5e-49b7-a618-048261f0c47a ClientRequestId: 69a0f44c-ff5e-49b7-a618-048261f0c47a

v2mounish avatar Mar 29 '23 11:03 v2mounish

date: 2023-01-17T13:46:39

The date from the error log is more than a month ago. Any chance you are able to replicate the error and share an update stack trace to help lookup the request?

andrueastman avatar Mar 29 '23 11:03 andrueastman

Hi

Now we are not getting specific error but its taking very long time to respond the email data with attachments,

so that client system will get timed out.

Thanks Nagaraj L

v2mounish avatar Mar 30 '23 11:03 v2mounish

Thanks for the extra information @v2mounish

As you are trying to fetch 1000 emails at a time with the attachments included which will be of different sizes this will be a very expensive operation and also result in a very large payload that may take time to download. To alleviate this, you can

  • Use a smaller page size maybe 100 at a time?
  • Increase the client timeout at the time of initializing the client.
graphClient.HttpProvider.OverallTimeout = TimeSpan.FromMinutes(5);

andrueastman avatar Mar 30 '23 14:03 andrueastman

Thanks for Quick Response

We have created the Web API and implemented the Microsoft Graph SDK menthods inside the API call, Hosted in our IIS Server.

Server API Code :

var graphClient = GraphAuthHelpers.GetAuthenticatedGraphClient(emailRequest.ClientUserName, emailRequest.ClientUserPassword); graphClient.HttpProvider.OverallTimeout = TimeSpan.FromMinutes(5); List<EmailResponse> lstEmailResponse = new List<EmailResponse>(); try { var inboxMessages = await graphClient.Me.Messages .Request() //.Select(m => new { m.Id, m.Subject, m.Attachments, m.From, m.Body }) .Top(1000) .Expand("attachments") .OrderBy("receivedDateTime asc") .GetAsync();

In the client system like Console application, Desktop application etc., we are using the RestClient and RestRequest RestSharp class and calling the API menthods using the below

Client System Code Example:

private static List<EmailResponse> ReadMail(string strURL) { RestClient client = new RestClient(strURL); RestRequest request = new RestRequest("ReadMail", Method.Get); request.AddHeader("Accept", "application/json");

        string ClientUserName = "***************";
        string ClientUserPassword = "**************";
        request.AddParameter("ClientUserName", ClientUserName);
        request.AddParameter("ClientUserPassword", ClientUserPassword);
                    
        var response = Task.Run(async delegate
        {
            await Task.Delay(TimeSpan.FromSeconds(0));
            return client.Execute(request).Content;
        });
        response.Wait();
        
        List<EmailResponse> lstEmailResponse = JsonConvert.DeserializeObject<List<EmailResponse>>(response.Result);

        return lstEmailResponse.ToList();

    }

// List<EmailResponse> lstEmailResponse = JsonConvert.DeserializeObject<List<EmailResponse>>(response.Result);

In the Above line of the code we are getting the request time out error or null values because when we debug the code in API code it takes lot of time and finaly return the correct values in API Code but in Client system we are getting the null response.

I hope you understand if anything please ask us.

So Could you pls suggest us the best solution to implement the same.

v2mounish avatar Mar 30 '23 16:03 v2mounish

Hey @v2mounish,

I'd suggest using a smaller page size for this as fetching 1000 items in one go will take time, especially with the attachments included. The client can then send a pageNumber to the endpoint so that you can split the results and get quicker response timses.

        // client can send this parameter
        var pageNumber = 0;


        var pageSize = 100;// get 100 items at a time
        var inboxMessages = await graphClient.Me.Messages
            .Request()
            //.Select(m => new { m.Id, m.Subject, m.Attachments, m.From, m.Body })
            .Top(pageSize) 
            .Skip(pageNumber * pageSize)
            .Expand("attachments")
            .OrderBy("receivedDateTime asc")
            .GetAsync();

andrueastman avatar Apr 03 '23 09:04 andrueastman

Ok Got it but here i have one doubts how do we find how many pageNumbers available in Inbox folder because we need to loop that many times.

do we have any specific menthod to find the max pageNumber of the Inbox.

**The Scenariois is we will receive 2K to 5K emails per hour's and we need to read all the emails at a time and process some task,

after that we are going to delete all the 2k or 5K emails one by one from Inbox through Microsoft Graph Delete method.

So I am thinking that sending pageNumber will realy works.

Please advice!**

Thanks Nagaraj L

v2mounish avatar Apr 04 '23 11:04 v2mounish

Using v4 you can calculate the total number of items of in the mailfolder as below.

            string requestUrl = graphClient.Users["user-id"]
                                            .MailFolders["mailfolder"]
                                            .Messages.AppendSegmentToRequestUrl("$count");
            HttpResponseMessage responseMessage = await new BaseRequest(requestUrl, graphClient, null)
                                                        .SendRequestAsync(null, CancellationToken.None);

            if (responseMessage.IsSuccessStatusCode)
            {
                string userCount = await responseMessage.Content.ReadAsStringAsync();
                var totalMessages = int.Parse(userCount);
            }

You can then use the number to calculate the number of pages you will have by dividing the number by the pageSize you choose.

This is much simpler in V5 as all you will need to do is to get the total items in the folder.

var totalItems = await graphClient.Users["{user-id}"].MailFolders["{mailFolder-id}"].Messages.Count.GetAsync();

andrueastman avatar Apr 05 '23 07:04 andrueastman

Hi,

As I have described earlier comments we have large number of emails per hour, example 2K to 5K emails per hours,

So to process 1 hours received emails its taking very long time,

Compare to SMTP for reading bulk emails, Microsoft.Graph is very slow..

v2mounish avatar Apr 09 '23 06:04 v2mounish