Reputation: 2211
I have an azure function (EventGrid trigger) that fires when a new blob gets created in the blob storage (Data Lake Storage Gen2). In this function I need to read the blob and deserialize it to an object (type known).
This is the method I use to get the CloudBlob:
public async Task<CloudBlob> GetCloudBlob(Uri uri)
{
var cloudBlockBlob = new CloudBlob(uri, _storageCredentials);
if (await cloudBlockBlob.ExistsAsync())
return cloudBlockBlob;
return null;
}
With this code I read the cloud blob
using var stream = await cloudBlob.OpenReadAsync();
await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri);
In the method HandleFile I call the DeserializeAsync method:
var model = await JsonSerializer.DeserializeAsync<RequestModel>(stream);
For some reason, the stream is some times empty (stream.Length is 0) resulting in this exception.
The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.
The funny thing is that, if I retry the process of this blob again, then the stream gets populated as expected.
Am I missing any wait condition, or can it be that the blob is still not yet fully written to the storage?
Upvotes: 0
Views: 747
Reputation: 8255
You can use a blob input binding for your EventGridTrigger function to get the blob stream, see the following code snippet:
public static async Task Run(JObject eventGridEvent, Stream body, ILogger log)
{
log.LogInformation($"{eventGridEvent}");
// ...
var model = await System.Text.Json.JsonSerializer.DeserializeAsync<RequestModel>(body);
// ...
await Task.CompletedTask;
}
and the function.json file:
{
"bindings": [
{
"type": "eventGridTrigger",
"name": "eventGridEvent",
"direction": "in"
},
{
"type": "blob",
"name": "body",
"path": "{data.url}",
"connection": "rk2019dlstg_STORAGE",
"direction": "in"
}
],
"disabled": false
}
Upvotes: 0
Reputation: 4870
Try converting await cloudBlockBlob.ExistsAsync()
to await cloudBlockBlob.ExistsAsync().ConfigureAwait(false)
, await cloudBlob.OpenReadAsync()
to await cloudBlob.OpenReadAsync().ConfigureAwait(false)
, await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri)
to await _requestFileHandler.HandleFile(stream, name, prefix, cloudBlob.Uri).ConfigureAwait(false)
and await JsonSerializer.DeserializeAsync<RequestModel>(stream)
to await JsonSerializer.DeserializeAsync<RequestModel>(stream).ConfigureAwait(false)
.
When the await
keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. We can avoid forcing continuations/callbacks back to the original context, and we do that by using ConfigureAwait(false)
. Not using ConfigureAwait(false)
causes performance issues and unreliability. Since, you are not using it, the stream
is sometimes in Caller context and sometimes in current context. The one in current context gives you correct results. You can visit devblogs.microsoft.com/dotnet/configureawait-faq for more information.
Upvotes: 0
Reputation: 20097
Serializing to the stream leaves the stream positioned at the end, so when you go to deserialize it, there's nothing to deserialize.
You need to add a line like:
stream.Position = 0;
after you serialize and before you deserialize.
Upvotes: 1