Skip to main content

One post tagged with "querystring"

View All Tags

Azure Functions and .NET 5: Query params, Dependency Injection, Bicep & Build

ยท 4 min read

The upgrade of Azure Functions from .NET Core 3.1 to .NET 5 is significant. There's an excellent guide for the general steps required to perform the upgrade. However there's a number of (unrelated) items which are not covered by that post:

  • Query params
  • Dependency Injection
  • Bicep
  • Build

This post will show how to tackle these.

title image showing name of post and the Azure Functions logo

Query params#

As part of the move to .NET 5 functions, we say goodbye to HttpRequest and hello to HttpRequestData. Now HttpRequest had a useful Query property which allowed for the simple extraction of query parameters like so.

var from = req.Query["from"]

HttpRequestData has no such property. However, it's straightforward to make our own. It's simply a matter of using System.Web.HttpUtility.ParseQueryString on req.Url.Query and using that:

var query = System.Web.HttpUtility.ParseQueryString(req.Url.Query);var from = query["from"]

Dependency Injection, local development and Azure Application Settings#

Dependency Injection is a much more familiar shape in .NET 5 if you're familiar with .NET Core web apps. Once again we have a Program.cs file. To get the configuration built in such a way to support both local development and when deployed to Azure, there's a few things to do. When deployed to Azure you'll likely want to read from Azure Application Settings:

screenshot of Azure Application Settings

To tackle both of these, you'll want to use AddJsonFile and AddEnvironmentVariables in ConfigureAppConfiguration. A final Program.cs might look something like this:

using System;using System.Threading.Tasks;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;
namespace MyApp{    public class Program    {        public static Task Main(string[] args)        {            var host = new HostBuilder()                .ConfigureAppConfiguration(configurationBuilder =>                     configurationBuilder                        .AddCommandLine(args)                        // below is for local development                        .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)                        // below is what you need to read Application Settings in Azure                        .AddEnvironmentVariables()                )                .ConfigureFunctionsWorkerDefaults()                .ConfigureServices(services =>                {                    services.AddLogging();                    services.AddHttpClient();                })                .Build();
            return host.RunAsync();        }    }}

With this approach in place, when the application runs, it should construct a configuration driven by all the providers required to run our application.

Bicep#

When it comes to deploying to Azure via Bicep, there's some small tweaks required:

  • appSettings.FUNCTIONS_WORKER_RUNTIME becomes dotnet-isolated
  • linuxFxVersion becomes DOTNET-ISOLATED|5.0

Applied to the resource itself the diff looks like this:

resource functionAppName_resource 'Microsoft.Web/[email protected]' = {  name: functionAppName  location: location  tags: tags_var  kind: 'functionapp,linux'  identity: {    type: 'SystemAssigned'  }  properties: {    serverFarmId: appServicePlanName_resource.id    siteConfig: {      http20Enabled: true      remoteDebuggingEnabled: false      minTlsVersion: '1.2'      appSettings: [        {          name: 'FUNCTIONS_EXTENSION_VERSION'          value: '~3'        }        {          name: 'FUNCTIONS_WORKER_RUNTIME'-          value: 'dotnet'+          value: 'dotnet-isolated'        }        {          name: 'AzureWebJobsStorage'          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(resourceId('Microsoft.Storage/storageAccounts', storageAccountName), '2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'        }      ]      connectionStrings: [        {          name: 'TableStorageConnectionString'          connectionString: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(resourceId('Microsoft.Storage/storageAccounts', storageAccountName), '2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'        }      ]-      linuxFxVersion: 'DOTNETCORE|LTS'+      linuxFxVersion: 'DOTNET-ISOLATED|5.0'      ftpsState: 'Disabled'      managedServiceIdentityId: 1    }    clientAffinityEnabled: false    httpsOnly: true  }}

Building .NET 5 functions#

Before signing off, there's one more thing to slip in. When attempting to build .NET 5 Azure Functions with the .NET SDK alone, you'll encounter this error:

The framework 'Microsoft.NETCore.App', version '3.1.0' was not found.

Docs on this seem to be pretty short. The closest I came to docs was this comment on Stack Overflow:

To build .NET 5 functions, the .NET Core 3 SDK is required. So this must be installed alongside the 5.0.x sdk.

So with Azure Pipelines you might have have something that looks like this:

stages:- stage: build  displayName: build  pool:    vmImage: 'ubuntu-latest'  jobs:  - job: BuildAndTest    displayName: 'Build and Test'    steps:    # we need .NET Core SDK 3.1 too!    - task: [email protected]      displayName: 'Install .NET Core SDK 3.1'      inputs:        packageType: 'sdk'        version: 3.1.x
    - task: [email protected]      displayName: 'Install .NET SDK 5.0'      inputs:        packageType: 'sdk'        version: 5.0.x
    - task: [email protected]      displayName: "function app test"      inputs:        command: test            - task: [email protected]      displayName: "function app build"      inputs:        command: build        arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/MyApp'            - task: [email protected]      displayName: 'function app publish'      inputs:        command: publish        arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/MyApp /p:SourceRevisionId=$(Build.SourceVersion)'        publishWebProjects: false        modifyOutputPath: false        zipAfterPublish: true
    - publish: $(Build.ArtifactStagingDirectory)/MyApp      artifact: functionapp

Have fun building .NET 5 functions!