msgraph-sdk-dotnet
msgraph-sdk-dotnet copied to clipboard
Long Running Operation Handler Work
After about 10-50 copy operations I get
Error = {Code: generalException Message: An error occurred in the data store.
I assume that there is some contention because the copy operation is async.
I would like to perform the copy operations sequentially but I cannot monitor the copy operation as described in the API's documentation.
[x] Expected behavior [x] Actual behavior [x] Steps to reproduce the behavior
Expected behavior
Return an interface to monitor the progress of the copy operation.
See documentation:
Copy a DriveItem Working with APIs that make take a long time to complete
Actual behavior
The copy operation returns Task<DriveItem>
and the Task's result is always NULL.
There was an issue regarding the copy operation and this behavior:
Steps to reproduce the behavior
var copy = await _graphClient.Drive.Items[item.Id].Copy(newName, new ItemReference { DriveId = folder.ParentReference.DriveId, Id = folder.Id }).Request().PostAsync();
copy
is always NULL. There is no way to know when the copy operation actually finishes.
I need to check whether the Graph returns the location header so that a subsequent request can be made to monitor the copy status. Still, if it is returned, we'll need to expose the response headers. That is planned work.
2121286
Workaround for sending :
var implReq = ((DriveItemCopyRequest) req);
implReq.Method = "POST";
using(var response =
await implReq.SendRequestAsync(implReq.RequestBody, CancellationToken.None,
System.Net.Http.HttpCompletionOption.ResponseContentRead).ConfigureAwait(false)) {
if (response.IsSuccessStatusCode) {
//… do polling with response.Header.Location status
}
}
Please also note that AsyncMonitor does not work with current graph API. You should do your own polling and wait for "completed" result to grab copied item id.
To complete, my forked polling method:
private async Task<AsyncOperationStatus> pollForOperationCompletionAsync(string monitorUrl, IProgress<AsyncOperationStatus> progress, CancellationToken cancellationToken) {
AsyncOperationStatus asyncOperationStatus = null;
while (!cancellationToken.IsCancellationRequested) {
using(var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, monitorUrl)) {
using(var responseMessage = await graphClient.HttpProvider.SendAsync(httpRequestMessage).ConfigureAwait(false)) {
using(var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false)) {
asyncOperationStatus = graphClient.HttpProvider.Serializer.DeserializeObject<AsyncOperationStatus>(responseStream);
if (asyncOperationStatus == null) {
throw new ServiceException(
new Error {
Code = "generalException",
Message = "Error retrieving monitor status."
});
}
if (string.Equals(asyncOperationStatus.Status, "cancelled", StringComparison.OrdinalIgnoreCase)) {
return asyncOperationStatus;
}
if (string.Equals(asyncOperationStatus.Status, "failed", StringComparison.OrdinalIgnoreCase) ||
string.Equals(asyncOperationStatus.Status, "deleteFailed", StringComparison.OrdinalIgnoreCase)) {
object message = null;
if (asyncOperationStatus.AdditionalData != null) {
asyncOperationStatus.AdditionalData.TryGetValue("message", out message);
}
throw new ServiceException(
new Error {
Code = "generalException",
Message = message as string
});
}
if (progress != null) {
progress.Report(asyncOperationStatus);
}
if (string.Equals(asyncOperationStatus.Status, "completed", StringComparison.OrdinalIgnoreCase))
return asyncOperationStatus;
}
}
}
await Task.Delay(CoreConstants.PollingIntervalInMs, cancellationToken).ConfigureAwait(false);
}
return asyncOperationStatus;
}
Long running operation support needs to be implemented as a piece of middleware. This code above is a good starting point.
Hello,
I tried pfresnay's workarounds for sending and polling. I got a monitor uri that works in the browser. Unfortunately in polling method, the call of _graphClient.HttpProvider.SendAsync method throws a NullReferenceException at Microsoft.Graph.HttpProvider.<SendAsync>d__18.MoveNext(), System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw().
I read something nebulous about missing credentials. Do I have to add some scopes to the azure enterprise application? This HttpRequest does not call the Graph Rest Api but in my case the Sharepoint Rest Api. Scopes "User.Read", "Sites.Manage.All" are set.
Thank you.