Search code examples
azureazure-webjobsazure-webjobssdkazure-storage-queues

Azure WebJob ProcessQueueMessage fails to read CloudQueueMessage


In summary, I have an Azure Web Job linked to a CloudQueue via the WebJobs SDK's ProcessQueueMessage mechanism, using the CloudQueueMessage parameter type. This gives me a FunctionInvocationException when triggered via QueueTrigger.


Detail

The item is added successfully to the CloudQueue using the AddMessageAsync method:

await queue.AddMessageAsync(new CloudQueueMessage(JsonConvert.SerializeObject(myObject)));

And appears on the queue as the expected JSON representation of my object (from Message Text Preview in Cloud Explorer):

{"EmailAddress":"example@mail.com",
 "Subject":"Test",
 "TemplateId":"00-00-00-00-00",
 "Model":{"PropertyName1":"Test1","PropertyName2":"Test2"}
 }

However, when the ProcessQueueMessage method is triggered:

public static async void ProcessQueueMessage(
    [QueueTrigger(queueName)] CloudQueueMessage message, TextWriter log)

... I get a FunctionInvocationException:

Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueMessage ---> System.InvalidOperationException: Exception binding parameter 'message' ---> System.ArgumentNullException: String reference not set to an instance of a String.
 Parameter name: s
 at System.Text.Encoding.GetBytes(String s)
 at Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage.get_AsBytes() in c:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\Common\Queue\CloudQueueMessage.Common.cs:line 146
 at Microsoft.Azure.WebJobs.Host.PropertyHelper.CallPropertyGetter[TDeclaringType,TValue](Func`2 getter, Object this)
 at Microsoft.Azure.WebJobs.Host.PropertyHelper.GetValue(Object instance)
 at Microsoft.Azure.WebJobs.Host.Bindings.BindingDataProvider.GetBindingData(Object value)
 at Microsoft.Azure.WebJobs.Host.Queues.Triggers.UserTypeArgumentBindingProvider.UserTypeArgumentBinding.BindAsync(IStorageQueueMessage value, ValueBindingContext context)
 at Microsoft.Azure.WebJobs.Host.Queues.Triggers.QueueTriggerBinding.<BindAsync>d__0.MoveNext()
 --- End of stack trace from previous location where exception was thrown ---
 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
 at Microsoft.Azure.WebJobs.Host.Triggers.TriggeredFunctionBinding`1.<BindCoreAsync>d__7.MoveNext()
 --- End of inner exception stack trace ---
 at Microsoft.Azure.WebJobs.Host.Executors.DelayedException.Throw()
 at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<ExecuteWithWatchersAsync>d__31.MoveNext()
 --- End of stack trace from previous location where exception was thrown

This seems to suggest that the message parameter is failing to read the JSON into the CloudQueueMessage object ... but it doesn't appear that it's something I have control of.

Does anyone have any suggestions as to why this might be happening?


Version Info

Microsoft.Azure.Webjobs 1.1.1

WindowsAzure.Storage 6.2.2-preview

DNX 4.5.1

Background

Troy Hunt - Web Job primer

MSDN - How to... article


Solution

  • change your ProcessQueueMessage to accept a string (which is what you actually passing to the CloudQueuMessage), then take that and deserialize it to your object:

    public static async void ProcessQueueMessage(
        [QueueTrigger(queueName)] string message, TextWriter log)
    {
       JsonConvert.DeserializeObject<YourObjectType>(json);
    }
    

    Or event better, if this is a POCO object then all you have to do is to use that instead:

    public static async void ProcessQueueMessage
    (
       [QueueTrigger(queueName)] YourObjectType message, 
       TextWriter log
    )
    {      //....    }
    

    Update: Although it would be nice to get the entire thing in a CloudQueueMessage object, but to get the properties that is sent in the CloudQueueMessage you can add the following parameters to the webjob method:

    public static async void ProcessQueueMessage(
            [QueueTrigger(queueName)] string logMessage, 
            DateTimeOffset expirationTime,
            DateTimeOffset insertionTime,
            DateTimeOffset nextVisibleTime,
            string id,
            string popReceipt,
            int dequeueCount,
            string queueTrigger,
            CloudStorageAccount cloudStorageAccount,
            TextWriter logger)
        {
            logger.WriteLine(
                "logMessage={0}\n" +
            "expirationTime={1}\ninsertionTime={2}\n" +
                "nextVisibleTime={3}\n" +
                "id={4}\npopReceipt={5}\ndequeueCount={6}\n" +
                "queue endpoint={7} queueTrigger={8}",
                logMessage, expirationTime,
                insertionTime,
                nextVisibleTime, id,
                popReceipt, dequeueCount,
                cloudStorageAccount.QueueEndpoint,
                queueTrigger);
        }