This post’s main objective was originally about completing the initial skeleton of uploading an image from a web page and generating a thumbnail from an Azure Web Job using Azure Blob Storage and Azure Queues, but it turned into a pretty large refactoring in anticipation of having something a bit more realistic to eventually post to GitHub. So, I’ll devote the first part of the post to a brief review of the most significant changes and then introduce the Azure Web Job into the mix and finally, I’ll retrieve the message from the Azure Queue and show its name in the console. I’ll then devote Azure Bit #5 to processing the original image and generating a thumbnail to complete the initial skeleton of our Image Manipulator application.
I’m not sure why I originally chose to go with serializing my UploadedImage to a ByteArray before inserting it into the Azure Queue, but I’ve now simplified things a bit and switched over to serializing my UploadedImage as JSON. This allows me to drop the ByteArray extension methods that I previously added to the project and it buys me some nice auto-hydration of my UploadedImage later when my processing function is called in the Azure Web Job. Revisiting the AddMessageToQueueAsync method in my QueueService, we now convert the UploadedImage to JSON instead of a ByteArray. Note that you’ll need to include the Newtonsoft.JSON Nuget package for the JSON serialization if it’s not already present in your web project.
In addition, I can revert the Data property in my UploadedImage class to an Auto Property since I no longer need to bother with the [NonSerialized] attribute. I can also remove the [Serialized] attributes from my models. Instead, I will place a [JsonIgnore] attribute on my Data property and it will be skipped in the JSON serialization process. I’ve also removed the previous hard-coded inclusion of one thumbnail from my constructor for UploadedImage. My models now look like this:
I had planned to get through this Azure Bits series without Dependency Injection, but I decided I missed it and didn’t want to promote the practice of new-ing up the dependencies directly. So, I’ve added Ninject to the solution and replaced all direct instantiation of my dependencies. It’s super easy to add Ninject via Nuget and there’s even a Ninject.MVC5 package available to get you started on wiring the dependencies. This package will insert a configuration file for Ninject in your App_Start directory. The important changes I needed to make to the default Ninject configuration file were to the RegisterServices method, where the actual wiring of the dependencies takes place. You can see that I am also injecting the appSettings and connectionString into my dependencies allowing me to mock these more easily for unit tests.
With Ninject now in place, we add a constructor to HomeController so we can inject the dependencies via Constructor Injection. Also, I moved the creation of the default thumbnail to the controller for now. Other than these changes, the HomeController remains unchanged.
After making these changes, you should be able to run the application and upload an image to Azure Blog Storage and the new JSON version of your UploadedImage object should be sitting in the Azure Queue as a message waiting to be picked up by your Azure Web Job. Taking a peek at the images message queue confirms that this is indeed the case:
After that quick refactoring detour, we are now ready to get on with the business of creating our Azure Web Job which will monitor our Azure Queue for UploadedImage messages and will automatically kick off the processing of thumbnails any time a new UploadedImage is added to the Queue. There is a nice Azure Web Job project type available in Visual Studio. Simply right-click the solution and choose Add a New Project and look under the Cloud folder for the Azure Web Job project.
This will create a console application with a few hooks in place for the Azure Web Job. You’ll first see the standard Program.cs. Here there will be some default code in the Main method that will create the JobHost and then run it. This default code assumes that you have connection strings in place either in Azure or in your app.config that are named explicitly as AzureWebJobsDashboard and AzureWebJobsStorage. If it can’t find these connection strings based on the default implementation of the Main method, you’ll get errors. There is an overloaded constructor for JobHost that takes an instance of JobHostConfiguration that allows more control over the connection strings and other configuration information for this Web Job. I prefer to explicitly set the connection strings and then I can name them whatever I want. So, my Main method looks like this:
The other important file that gets created by the Azure Web Jobs template is the functions.cs file. This is the file that contains the function (or functions) that listen to specific Azure queues and are called when new messages are added. There is only function added in the default implementation. I am going to replace the parameters with ones more appropriate for our task at hand. You’ll notice that the first parameter in my implementation is an UploadedImage and you’ll note that the parameter is marked with a [QueueTrigger(“images”)] attribute. This is what tells Azure to call this method when the Queue named “images” enqueues a new message.
Moving to the second parameter, originalImageStream… this parameter is marked with the [Blob(“images/{Name}”)] attribute. If you recall, the UploadedImage class has a Name property that contains the name of the original image that we inserted into Azure Blob Storage. When ProcessQueueMessageAsync is called, Azure hydrates the first parameter, UploadedImage, from the JSON message we inserted into the Queue and then it looks at the Name property (or whatever property we specify in the braces of our Blob attribute). With this Blob attribute, we are telling Azure to go to my container named “images” and get the image that matches the uploadedImage.Name property and then deliver this to me as a stream in my processing method. This is very helpful as the first thing you’d want to do without this automatic delivery of the source stream is go to Azure Blob Storage and fetch it. This is what my ProcessQueueMessageAsync method looks like now.
If you right-click your Web Job project and choose Publish as Azure Web Job, your Web Job should get published to your Azure Web Site. You can double-check that in the Azure Portal by going to Web Apps and selecting Web Jobs.
Ideally, you would want your Web Job running continuously so that it would be called automatically when a new image is added, but depending on the type of Azure account that you have, you may not have the ability to run your Web Job continuously. However, you can still test it by right-clicking the project in the Solution Explorer and choosing Debug > Start New Instance. Assuming you have a message in your queue, you should see something similar to the below and you should see that the message is now removed from your Azure Queue.
Now that we are successfully fetching our UploadedImage message and our original image, we just need to process the received image file into the thumbnail image(s) specified in our UploadedImage.Thumbnails collection. In the next Azure Bit, I’ll introduce an ImageProcessor and add the functionality to generate a thumbnail and save it to Azure Blob Storage. If you missed the other posts in this series, you can start with Azure Bits #1 – Up and Running.
Cloud management is difficult to do manually, especially if you work with multiple cloud…
Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…
https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…
FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…
Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…
In the intricate landscape of modern business, compliance is both a cornerstone of operational integrity…
View Comments
Part 5 published yet? What is the point of having messageId in AddMessageToQueueAsync?
Part 5 published yet? What is the point of having messageId in AddMessageToQueueAsync?