Since Microsoft released a preview version of Visual Studio Tools for Azure Functions in December, 2016, it’s been reported very buggy. Current roadmap says the tooling won’t be unveiled until .NET Standard 2.0 is released. Therefore, Azure App Service Team published an article to utilise ASP.NET Web Application projects in the meantime. Even though the approach requires a lot of hands for initial setup, this is literally the only way to play Azure Functions on Visual Studio without the tooling.
In this post, as a complementary one to the article, we are going to build a simple Web API using HTTP Triggers and implement a simple CQRS pattern using Queue Triggers by precompiled approach. I hope this post would offer more details.
The sample code used for this post can be found here.
SIDE NOTES:
- We use VS2015 in this post. If you want to use VS2017, that would be fine.
- We only use .NET Framework version 4.6 as Azure Functions uses this version.
Benefits of Precompiled Azure Functions
There are several benefits when we use Azure Functions that come as precompiled .dll
files:
- We can use full features on Visual Studio, including IntelliSense.
- We can easily write unit test codes.
- We can easily attach function codes to existing CI/CD pipelines.
- We can easily migrate the existing codebase with barely modifying them.
- We don’t need
project.json
for NuGet package management. - We can reduce the total amount of cold start time by removing compiling on-the-fly when requests hit to the Functions.
Let’s find out a scenario we’re using in this post.
Web APIs to Create or View Product Details
We’re creating two Web API endpoints – one to add/update product details, and the other to view product details. We also add another Function code using Azure Storage Queue to implement CQRS patterns.
Entity Framework Code-First Approach for Product Details
First of all, we’re creating a class library to take care of database transaction using Entity Framework. Here are a simple product class and database context.
We can find this at the PrecompiledSample.EntityModels
project.
API Request/Response Object
Next, we create a simple DTO, ProductModel.cs
not to directly expose database entity.
We can find this at the PrecompiledSample.Models
project.
Service Layer
Lastly, When we develop Web API applications, we usually implement service layers that are called by controllers. Of course, there’s no controller in Azure Functions, those service layers are instantiated within the Run
method of Azure Functions code.
We can find this at the PrecompiledSample.Services
project.
So far, have you found out any difference? Probably not. Now, it’s time to write Azure Functions code.
Creating Web Application Project for Azure Functions
As Azure Functions basically run on top of an Azure Web App instance, we can start with an empty ASP.NET Web Application project, targeting .NET Framework 4.6, within Visual Studio.
Of course there’s nothing in the project, except packages.config
and Web.config
.
Install NuGet packages below to run Azure Functions within our Web Application project:
For our simple Web API, for database transactions, we need more NuGet packages:
We’ve completed our basic setup for Azure Functions code. Let’s move on.
Writing Azure Functions
Let’s think about the Azure Functions app structure. Each folder of the Web Application project works as an individual function. Therefore, we can simply create folders and put function.json
in each of them. Let’s have a look.
AddProductHttpTrigger
: This is an HTTP Trigger works as an API endpoint, which takes POST requests, sends the message body to Azure Storage Queue and returns HTTP Status Code of 202 (Accepted) right away.AddProductQueueTrigger
: This is a Queue Trigger that handles database transaction, which watches Azure Storage Queue, takes messages from there and process them to the database.GetProductHttpTrigger
: This is an HTTP Trigger works as an API endpoint, which takes GET request and returns messages with HTTP Status Code of 200 (OK).
Here’s a high-level diagram how POST request is processed through Functions.
We mentioned about function.json
just above. What do we put inside it, then? The easiest way to check its content will be to just create a function code on Azure Functions instance. Let’s have a look.
AddProductHttpTrigger
If we create an HTTP Trigger function from Azure Portal, we can find the function.json
. It looks like:
There are two bindings defined – one for input binding and the other for output binding. The input binding has a type of httpTrigger
, while the output binding has a type of http
. Copy all and paste it into the function.json
in our Visual Studio project. And add another output binding with the type of queue
. Also, we need to provide precompiled .dll
file’s information like below:
As defined above, we need AddProductHttpTrigger.cs
.
Copy the code from Run.csx
of Azure Portal and paste it into the file, then modify it like below:
How can we find out the differences between our code and Run.csx
?
- There’s no
#r
directive. This is because the function code is not a script anymore. - There are namespace and class name definition. Previously in script-style function code didn’t allow those definitions around the
Run
method. - Message body from the request is directly sent to the queue.
- At the same time, it returns HTTP Status Code of 202 (Accepted). This is more semantic than returning HTTP Status Code of 200 (OK) as it’s the non-blocking and async function.
We’ve now created the API endpoint to create a resource, which is corresponding to the C
of the CQRS pattern.
AddProductQueueTrigger
When we create a sample Queue Trigger function code, we can see the function.json
. Copy and paste it into our function.json
in Visual Studio and define the entry point like:
Create the AddProductQueueTrigger.cs
file like below:
Copy the code from Run.csx
and paste it to the file and modify it like below:
This function actually does database transactions. Let’s have a look:
- While normal web applications refer to
Web.config
for database connection string, Azure Functions is not designed to read from it. Instead, it uses the App Settings blade of the Azure Function Portal that defines database connection string. In spite of this fact, we can still write code the same way,ConfigurationManager.ConnectionStrings["NAME"].ConnectionString
. - If we want to use additional configurations, use
ConfigurationManager.AppSettings["KEY"]
that is defined in the App Settings blade of the Azure Functions Portal. We can’t use custom configuration section for this purpose. Alternatively, if you really want to custom configuration settings, create a JSON file,mysettings.json
for example, and deserialise it usingJson.NET
. - It uses the service layer instance that is written in another project,
PrecompiledSample.Services
. If you are concerned at dependencies, consider the service locator pattern. Testing Precompiled Azure Functions explained well how to apply service locator pattern in Azure Functions.
So far, we have written function codes for resource creation.
GetProductHttpTrigger
Copy the function.json
from the HTTP Trigger function created earlier in the portal, paste it into the function.json
in Visual Studio, and modify it like:
Then, create the GetProductHttpTrigger.cs
file.
Here’s the code for it:
Let’s have a look at the code.
- This function code takes the GET request, so it looks up the
id
query parameter from the querystring and use it as theProductId
value. - It uses the service layer instance that is written in another project,
PrecompiledSample.Services
. - It returns the resource details with HTTP Status Code of 200 (OK).
So far, we have created the resource lookup API, corresponding to the Q
of the CQRS pattern. we’ve completed all necessary function codes. If we integrate this Web Application project with Azure Functions CLI, we can easily perform testing and debugging in our local environment with the same development experiences that Visual Studio offers.
Setting Up Debugging Environment for Azure Functions within Visual Studio
We need two more tools for our local debugging experience within Visual Studio.
In order to install Azure Functions CLI, we can simply run the command, npm install --global azure-functions-cli
. Make sure that those two tools only work in Windows at the time of writing. Once both are installed, open the project property window like below:
Move to the Web
tab and enter the necessary information.
When we integrate Azure Functions CLI with the Web Application project, there are a few points that we need to make sure.
- The installed location of
node.js
might be different:- If it is downloaded from
https://nodejs.org
, the CLI location would beC:\Users\[USERNAME]\AppData\Roaming\npm\node_modules\azure-functions-cli\bin\func.exe
. - If it is installed through NVM, the CLI would be located at
C:\Program Files\nodejs\node_modules\azure-functions-cli\bin\func.exe
.
- If it is downloaded from
- For
Command line arguments
, its value would behost start
to run WebJobs host in our local machine. Working directory
needs to have the absolute path of the Web Application project where Azure Functions codes reside.
Unfortunately, we can’t use
%
environment variables here.
Finally, we need to add two .json
files – appsettings.json
and host.json
. Unless necessary, host.json
is empty. On the other hand, we need to put some details into appsettings.json
like below:
As we’re using Azure Storage Emulator, the value, UseDevelopmentStorage=true
, is used. As well as for the database connection string is defined in this file.
Now, it’s time for debugging! Set the Web Application project as a startup project and punch the F5
key, then we’ll be able to see the command prompt window that is running Azure Functions CLI.
Let’s send a POST request through a REST API testing tool like Postman. As we can see the screenshot above, the endpoint URL for resource creation is http://localhost:7071/api/AddProductHttpTrigger
, so send a POST request like below:
Then, the code stops at the break point where we setup in Visual Studio.
How did you feel it? We can use the same development experience for Azure Functions development. Now, it’s time for deployment to the actual Azure Functions instance.
Deploying Azure Functions
We have developed Azure Function codes within a Web Application project. That means we will have the same deployment experience.
When we choose the publish menu like above, we can select either Azure App Service option
or import publish profile settings file downloaded from the Azure Function instance.
Once we complete deployment, we can confirm on Azure Function portal that all function codes have been successfully deployed. Please note that we don’t need to build separate web application project for each function, but just put everything in one web application project, which would be sufficient.
Once deployed, let’s send a POST request to the endpoint for the AddProductHttpTrigger
function. Request body will flow the diagram mentioned before.
Once data has been processed, let’s check the result on the Azure Function side:
And here’s the database query result.
So far, we have built a sample Azure Functions code using Web Application project on Visual Studio. We have used .dll
files instead of .csx
files for Function codes. With these precompiled .dll
library files, we have performed debugging and deployment as well. How did you guys find out? Is it easier to use? Does it give you the same development experiences? It may not be easy at the first glance. However, because this is the same approach that we develop a web application, we can easily get used to it.
Hope this post will help you write Azure Function codes with full support by Visual Studio.