We recently an alpha for WebJobs SDK (aka AzureJobs , and internally codenamed “SimpleBatch”). In this blog entry, I wanted to explain how Triggers, Bindings, and Route Parameters worked in AzureJobs.

A function can be “triggered” by some event such as a new blob, new queue message, or explicit invocation. JobHost (in the Microsoft.WindowsAzure.Jobs.Host nuget package) listens for the triggers and invokes the functions.

The trigger source also provides the “route parameters”, which is an extra name-value pair dictionary that helps with binding. This is very similar to WebAPI / MVC. The trigger event provides the route parameters, and then the parameter can be consumed in other bindings:

1. Via a [BlobOutput] parameter 
2. Via an explicit parameter capture.

 

Trigger on new blobs

Example usage:

This happens when the first attribute is [BlobInput] and the function gets triggered when a new blob is detected that matches the pattern.  

        public static void ApplyWaterMark(
            [BlobInput(@"images-output/{name}")] Stream inputStream,
            string name,
            [BlobOutput(@"images-watermarks/{name}")] Stream outputStream)
        {
            WebImage image = new WebImage(inputStream);
            image.AddTextWatermark(name, fontSize: 20, fontColor: "red");
            image.Save(outputStream);
        }

When does it execute?

The triggering system will compare timestamps for the input blobs to timestamps from the output blobs and only invoke the function if the inputs are newer than the outputs. This simple rules makes the system very easy to explain and prevents the system from endlessly re-executing the same function.

Route parameters:

In this case, the route parameter is {name} from BlobInput, and it flows to the BlobOutput binding. This pattern lends itself nicely for doing blob-2-blob transforms.

The route parameter is also captured via the “name” parameter. If the parameter type is not string, the binder will try to convert via invoking the TryParse method on the parameter type. This provides nice serialization semantics for simple types like int, guid, etc. The binder also looks for a TypeConverter, so you can extend binding to your own custom types.

In WebAPI, route parameters are provided by pattern matching against a URL. In AzureJobs, they’re provided by pattern matching against a BlobInput path (which is morally similar to a URL). This case was implemented first and so the name kind of stuck.

 

Trigger on new queue message

Example usage:

This happens when the first attribute is [QueueInput].

        public static void HandleQueue(
            [QueueInput(queueName : "myqueue")] CustomObject obj,
            [BlobInput("input/{Text}.txt")] Stream input,
            int Number,
            [BlobOutput("results/{Text}.txt")] Stream output)
        {
        }
 
    public class CustomObject
    {
        public string Text { get; set; }

        public int Number { get; set; }
    }
 

The function has both a QueueInput and BlobInput, but it triggers when the Queue message is detected and just uses the Blob input as a resource while executing.

When does it execute?

This function executes when a new queue message is found on the specified queue. The JobHost will keep the message invisible until the function returns (which is very handy for long running functions) and it will DeleteMessage for you when the function is done.

Route parameters:

In this case, the route parameters are the simple properties on the Queue parameter type  (so Text and Number). Note that since the queue parameter type (CustomObject) is a complex object, it will get deserialized using JSON.net.  Queue parameter types could also be string or byte[] (in which case they bind to the CloudQueueMessage.AsString and AsBytes).

The usage of parameter binding here may mean your function body doesn’t even need to look at the contents of the queue message.

 

Trigger when explicitly called via JobHost.Call().

Example usage:

You can explicitly invoke a method via JobHost.Call().

            JobHost h = new JobHost();
            var method = typeof(ImageFuncs).GetMethod("ApplyWaterMark");
            h.Call(method, new { name = "fruit.jpg" });

When does it execute?

I expect the list of possible triggers to grow over time, although JobHost.Call() does provide tremendous flexibility since you can have your own external listening mechanism that invokes the functions yourself. EG, you could simulate a Timer trigger by having your own timer callback which does JobHost.Call().

You can use JobHost.Call() to invoke a method that would normally be triggered by BlobInput.

You can also suppress automatic triggering via a "[NoAutomaticTrigger]” attribute on the method. In that case, the function can only be invoked via JobHost.Call().

Route Parameters:

The Call() method takes an anonymous object that provides the route parameters. In this case, it assigned “name”  as “fruit,jpg”. The single route parameter will allow 3 normal parameters of ApplyWaterMark to get bound.

 

Disclaimers

AzureJobs is still in alpha. So some of the rules may get tweaked to improve the experience.