Qiuzman
Qiuzman

Reputation: 1761

How to avoid "Cannot access a disposed object" MemoryStream errors in async lambda functions

I have an issue that I can solve by running my code synchronously and it works great but to reduce wait time for my users I need to run async for this method as I have a few other tasks that need to run as well but I omitted for testing purposes but use the same procedures. I get an error in my Azure download method (DriveItemDownloadAsync) I created as shown:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'IServiceProvider'.

at Microsoft.Extensions.DependencyInjection.ServiceLookup.ThrowHelper.ThrowObjectDisposedException()
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
at Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost.GetEffectiveAuthenticationScheme(String authenticationScheme)
at Microsoft.Identity.Web.TokenAcquisitionAspnetCoreHost.GetOptions(String authenticationScheme, String& effectiveAuthenticationScheme)
at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(IEnumerable1 scopes, AuthorizationHeaderProviderOptions downstreamApiOptions, ClaimsPrincipal claimsPrincipal, CancellationToken cancellationToken)
at Microsoft.Identity.Web.TokenAcquisitionAuthenticationProvider.AuthenticateRequestAsync(HttpRequestMessage request)
at Microsoft.Graph.AuthenticationHandler.SendAsync(HttpRequestMessage httpRequestMessage, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Microsoft.Graph.HttpProvider.SendRequestAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)}

The code I am using in my controller is as follows. If I remove the Task and make this run synchronously no error happens.

Task ReportTask = new Task(async () => {
                    var reportPathOneDrive = "path.docx";
                    var reportPathBox = "path.docx";

                    var FileInfo = await oneDrive.DriveItemInfoAsync(SharedDriveID, reportPathOneDrive, "path");

                    var memoryStream = await oneDrive.DriveItemDownloadAsync(SharedDriveID, reportPathOneDrive, "path");

                    MemoryStream memoryStreamBox = new MemoryStream();
                    memoryStream.CopyTo(memoryStreamBox);
                    memoryStream.Position = 0;
                    memoryStreamBox.Position = 0;

                    //add to email
                    var attachment = new ARMS.Models.FileAttachment
                    {
                        FileName = FileInfo.Name.ToString(),
                        ContentType = FileInfo.File.MimeType.ToString(),
                        ContentBytes = memoryStream.ToArray()
                    };

                    attachments.Add(attachment);

                    var uploadBox = await boxDrive.UploadFileAsync(BoxARMSFolderID, memoryStreamBox, reportPathBox);
                });

                ReportTask.Start();
                List<Task> documentTasks = new List<Task> { ReportTask };

                await Task.WhenAll(documentTasks);

The error will appear in this function here:

public async Task<MemoryStream> DriveItemDownloadAsync(string DriveID, string PathOrDriveItemID, string SearchType)
{
    var tries = 0;
    var maxRetries = 1;

    Stream response = null;
    var MemoryStream = new MemoryStream();

    while (tries <= maxRetries)
    {
        tries++;

        try
        {
            if (SearchType == "path")
            {
                response = await _graphServiceClient.Me.Drives[DriveID].Root
                                .ItemWithPath(PathOrDriveItemID)
                                .Content
                                .Request()
                                .GetAsync();
            }
            else if (SearchType == "id")
            {
                response = await _graphServiceClient.Me.Drives[DriveID]
                                .Items[PathOrDriveItemID]
                                .Content
                                .Request()
                                .GetAsync();
            }

            response.CopyTo(MemoryStream);
            MemoryStream.Position = 0;

            tries = maxRetries + 1;
        }
        catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
        {
            try
            {
                Console.WriteLine($"{svcex}");
                string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
                _consentHandler.ChallengeUser(initialScopes, claimChallenge);
            }
            catch (Exception ex2)
            {
                _consentHandler.HandleException(ex2);
            }
        }
    }

    return MemoryStream;
}

The response line where it actually goes to use MS Graph is what draws the error. I know I need to be careful with memory streams in async but I thought I was using them correctly in this sense with the lambda function. In fact this is the first time I have used lambda in a New Task() object so I am thinking that could be an issue?

Upvotes: 0

Views: 131

Answers (0)

Related Questions