Torniamo ancora sull’argomento “WebJobs” descrivendo brevemente le varie modalità di Triggering:
- Alla creazione di un nuovo Blob
- Alla ricezione di un nuovo Queue Message
- Esplicatamente tramite l’invocazione della funzione Call
Nei post precedenti (parte 1 e parte 2) abbiamo visto come attivare il Trigger del “WebJob” alla creazione di un nuovo Blob all’interno di un container specifico semplicemente utilizzando l’attributo [BlobInput]. Con le stesse modalità é possibile eseguire il Binding di una funzione invocata dall’instanza di JobHost al ricevimento di un messaggio in un specifica coda (Queue) come da esempio:
public static void ProcessBobByQueueMessage([QueueInput(@"inputqueue")] string message,
[BlobOutput(@"outputcontainer/resized_image.png")] Stream outputStream)
{
if (!message.Equals("go"))
return;
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(
System.Configuration.ConfigurationManager.ConnectionStrings["AzureJobsData"]
.ConnectionString);
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference("inputcontainer");
CloudBlob blob = container.GetBlobReference("TestPicture");
Stream inputStream = new MemoryStream();
blob.DownloadToStream(inputStream);
ResizeImage(inputStream, outputStream);
}
La funzione “ProcessBobByQueueMessage” é invocata al momento che un messaggio contenente il testo “go” arriva nella coda denominata “inputqueue”, come nei casi precedenti, creiamo un nuovo Blob nel container “outputcontainer” con il nome di “resized_image.png”. Da notare che nel codice utilizziamo le clasi del namespace Microsoft.WindowsAzure.StorageClient per lavorare con le classi che rappresentano lo storage di Windows Azure. Per testare il tutto é sufficiente caricare il Job sul nostro Web Site di test o premere F5 e lavoare “in locale”. Tramite l’Azure Storage Explorer possiamo creare un nuovo messaggio con la dicitura “go” come da figura:
E nel container di output avremo:
Dal codice si evince come il nome del Blob di ouput (e di input) sia fissato, ma per un’applicazione reale questo non é un comportamento desiderabile, cosi’, sarebbe auspicabile avere un comportamento piu’ dinamico utilizzando (ad esempio) una classe “Custom” serializzata in JSON, contenente la definizione dei file (Blob) di Input ed Output e passata come messaggio testo alla coda, tradotto in codice:
public static void ProcessBobByQueueCustomMessage([QueueInput(@"inputqueue")] ImageMessage message,
[BlobOutput(@"outputcontainer/{OutputName}")] Stream outputStream)
{
CloudStorageAccount storageaccount = CloudStorageAccount.Parse(
System.Configuration.ConfigurationManager.ConnectionStrings["AzureJobsData"]
.ConnectionString);
CloudBlobClient blobclient = storageaccount.CreateCloudBlobClient();
CloudBlobContainer container = blobclient.GetContainerReference("inputcontainer");
CloudBlob blob = container.GetBlobReference(message.InputName);
Stream inputStream = new MemoryStream();
blob.DownloadToStream(inputStream);
ResizeImage(inputStream, outputStream);
}
Da notare l’utilizzo del placeHolder “{OutputName}” specificato nel percorso (BlobPath) ed accettato come argomento dall’attributo BlobPath, uguale alla proprietà omonima della classe ImageMessage, ed utilizzata per definire il nome del Blob di output in questo modo:
public class ImageMessage
{
public string InputName { get; set; }
public string OutputName { get; set; }
}
Per un giro di test è sufficiente creare un nuovo messagio con il testo seguente:
Ottenendo:
Interessante è l’utilizzo dell’attributo [NoAutomaticTrigger()] che indica all’istanza di “JobHost” di non far scattare un particolare metodo (magari per evitare che il job venga attivato quando non tutte le condizioni necessiaro sono presenti). Questo attributo non va d’accordo con l’attributo [QueueInput(…)] in quanto a runtime solleva un’eccezione con il messaggio seguente: “Can't have QueueInput and NoAutomaticTrigger on the same function.”.
Per invocare programmaticamente una funzione è sufficiente utilizzare il metodo Call() della classe JobHost:
host.Call(methodInfo);
Dove “methodInfo” puo’ essere facilmente calcolato tramite Reflection.