Move ASP.NET Core to Azure Functions in 4 steps

Don't get me wrong, ASP.NET Core is awesome. I even wrote a book on it, and one should be using it for building any kind of cloud-first web applications. Azure Functions, on the other hand; is a fairly new offering from Microsoft Azure, and is focussing more on the serverless aspects of the cloud applications.

Unlike ASP.NET Core, Azure Functions can be written in many more languages, like C#, F#, PowerShell, Python, Java, JavaScript, and PHP. Some of the things that was quite a learning curve to wrap my head around is the use of static methods in C# and the lack of proper Dependency Injection out of the box, but on the flip side, the concept of bindings provides a convenient way to easily integrate with other cloud-native services, like Azure Service Bus or Azure Cosmos DB.

Furthermore, the version 2.x of the Azure Functions runtime, now runs on .NET Core, and the latest version includes support for instance methods and limited support for Dependency Injection.

Convert project from ASP.NET Core to Azure Functions

Migrating from a ASP.NET Core project to an Azure Functions project is not that hard, because the Azure Functions SDK v2.x is based on .NET Core, and moving code is just about some syntax changes and removing the host part of the API. The rest of the stack could stay exactly the same. There are some caveats to look out for at this stage, but I will run through them a bit later in this post.

In order for Dependency Injection to work with Azure Functions, the project needs to target netstandard2.0 instead of netcoreapp2.1.

Let's take a closer look on how we can convert a standard ASP.NET Core Web API project to an Azure Functions project. You can find the source code comparison for this typical ASP.NET Core project to Azure Functions here.

1. Remove Program.cs

One of the cool things about serverless, is that we don't have to worry about hosts and servers, so the first step is to basically remove them by deleting the Program.cs file.

When I refer to "removing Program.cs", I mean removing all the things that are host and server related. In this example it happens to be the whole class, but it may be that you have to update it due to other required dependencies, like logging and configuration.

2. Target Azure Functions SDK in project file

A few things need to change in the .csproj project file:

  • Set the Sdk attribute to Microsoft.NET.Sdk.
  • Remove the <AspNetCoreHostingModel/> element, and add the <AzureFunctionsVersion>v2</AzureFunctionsVersion> element.
  • Remove the Microsoft.AspNetCore package, and add the package Microsoft.NET.Sdk.Functions.
  • Optionally add the Microsoft.AspNetCore.Mvc.Core and Microsoft.AspNetCore.Mvc.Formatters.Json packages for the minimum MVC support.

Lastly, we need to add two files - a host.json file containing the following:

{
    "version": "2.0"
}

Also a local.settings.json file that contains the configuration for the local environment:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet"
    }
} 
It is important to point out that the runtime is determined by setting the FUNCTIONS_WORKER_RUNTIME setting or environmental variable to dotnet.

3. Tweak the Startup

The Startup.cs class needs to implement the IWebJobsStartup interface as well as the [assembly: WebJobsStartup(typeof(Startup))] attribute needs to be added:

[assembly: WebJobsStartup(typeof(Startup))]
namespace AwesomeApi  
{
    public class Startup : IWebJobsStartup
    {
        private static ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IPeopleRepository, PeopleRepository>();
        }

        public void Configure(IWebJobsBuilder builder)
        {
            ConfigureServices(builder.Services);
        }
    }
}

4. Functionize controller endpoints

For the most part, the controllers can be left as-is. The only thing left to do is remove the MVC specific components, like the Route, ApiController and Http* attributes, and decorate each endpoint with the FunctionName attribute, which will expose the specific function as an Azure Functions endpoint.

For the routing and HTTP methods to apply, we need to add a HttpTrigger attribute to a new parameter of type HttpRequest, and then specify the endpoint's route template and HTTP method constraints:

public class PeopleController : ControllerBase  
{
    //...
    [FunctionName(nameof(GetAll))]
    public IActionResult GetAll([HttpTrigger("get", Route = "people")]HttpRequest request)
    {
        //...
    }

    [FunctionName(nameof(Get))]
    public IActionResult Get([HttpTrigger("get", Route = "people/{id}")]HttpRequest request, int id)
    {
        //...
    }
}

That's it!

We now have successfully migrated an existing ASP.NET Core Web API to a pure Azure Functions app running on the Azure Functions V2 runtime.
It is important to note that, although it is currently working, instance methods and dependency injection isn't officially supported with the latest version and may result into side effects.

If you enjoyed this post, or have something else to add, please let me know and reach out.