Unable to bind ServiceBusTrigger BrokeredMessage members to Blob blobpath in Azure WebJobs SDK

455 views Asked by At

I am unable to add dynamic binding of any members of Service Bus BrokeredMessage using the Azure WebJobs SDK.

NOT working

// using properties on BrokeredMessage object
public static void ProcessDup([ServiceBusTrigger("dup")] **BrokeredMessage** q, [Blob("dup/{Label}")] string json, TextWriter log)
        {
            log.WriteLine($"Content(Id) {q.Label}\r\nBlob {json}");
            Console.WriteLine($"Content(Id) {q.Label}\r\nBlob {json}");
        }       

// using properties inside of the BrokeredMessage.Properties
public static void ProcessDup([ServiceBusTrigger("dup")] **BrokeredMessage** q, [Blob("dup/{JsonFilename}")] string json, TextWriter log)
        {
            log.WriteLine($"Content(Id) {q.JsonFilename}\r\nBlob {json}");
            Console.WriteLine($"Content(Id) {q.JsonFilename}\r\nBlob {json}");
        }       

It gives me the following messages when it processes messages from the service bus

  Function had errors. See Azure WebJobs SDK dashboard for details. Instance ID
is '0a957645-f042-4dca-a38f-643229fbbc21'
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while execut
ing function: Functions.ProcessDup ---> System.InvalidOperationException: Except
ion binding parameter 'json' ---> System.ArgumentNullException: Value cannot be
null.
Parameter name: bindingData
   at Microsoft.Azure.WebJobs.Host.Bindings.BindingDataPathHelper.ConvertParamet
ers(IReadOnlyDictionary`2 bindingData)
   at Microsoft.Azure.WebJobs.Host.Blobs.ParameterizedBlobPath.Bind(IReadOnlyDic
tionary`2 bindingData)
   at Microsoft.Azure.WebJobs.Host.Blobs.Bindings.BlobBinding.<BindAsync>d__0.Mo
veNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot
ification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Microsoft.Azure.WebJobs.Host.Triggers.TriggeredFunctionBinding`1.<BindCore
Async>d__7.MoveNext()
   --- End of inner exception stack trace ---
   at Microsoft.Azure.WebJobs.Host.Executors.DelayedException.Throw()
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<ExecuteWithWatche
rsAsync>d__31.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.HandleNonSuccessAndDebuggerNot
ification(Task task)
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<ExecuteWithLoggin
gAsync>d__2c.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.HandleNonSuccessAndDebuggerNot
ification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<ExecuteWithLoggin
gAsync>d__13.MoveNext()
   --- End of inner exception stack trace ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<ExecuteWithLoggin
gAsync>d__13.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.HandleNonSuccessAndDebuggerNot
ification(Task task)
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.<TryExecuteAsync>d
__1.MoveNext()

DOES work

public static void ProcessDup([ServiceBusTrigger("dup")] **TheDup** q, [Blob("dup/{JsonFilename}")] string json, TextWriter log)
        {
            log.WriteLine($"Content(Id) {q.JsonFilename}\r\nBlob {json}");
            Console.WriteLine($"Content(Id) {q.JsonFilename}\r\nBlob {json}");
        }       

The service bus message is based on this class

  [DataContract(Name = "TheDup", Namespace = "")]
    public class TheDup
    {
        [DataMember]
        public Guid Id { get; set; }
        public string JsonFilename => $"{Id}.json";
        [DataMember]
        public string Json { get; set; }
    }

The service bus message is added with

var client = QueueClient.CreateFromConnectionString(_queueConnectionString, _queueName);
var message = new BrokeredMessage(new TheDup() {Id = id, Json = sSourceData?.Length > 128 ? string.Empty : sSourceData})
{
 SessionId = sessionid == null ? id.ToString() : sessionid.ToString(),
 Label = $"{id}.json"
};
message.Properties.Add(new KeyValuePair<string, object>("JsonFilename", $"{id}.json"));
client.Send(message);
client.Close();
1

There are 1 answers

0
mathewc On

As you've discovered, you can only include binding parameters like dup/{JsonFileName} when binding to a POCO that defines those members. This is by design.

If this doesn't work for you and you truly need to do dynamic imperative binding, you can use IBinder to perform the blob binding in your code rather than in the parameter binding. Here's a simple example:

        public static void BlobIBinder(
            [QueueTrigger("persons")] Person persons, 
            IBinder binder)
        {
            var blobAttrib = new BlobAttribute("persons/" + persons.Name + "BlobIBinder");
            var writer = binder.Bind<TextWriter>(blobAttrib);
            writer.Write("Hello " + persons.Name);
    }