Guia para executar o Azure Functions em C# no modelo de trabalho isolado

Este artigo é uma introdução ao trabalho com o Azure Functions no .NET, usando o modelo de trabalho isolado. Esse modelo permite que seu projeto direcione versões do .NET independentemente de outros componentes de runtime. Para obter informações sobre versões específicas do .NET com suporte, confira versão com suporte.

Use os links a seguir para começar imediatamente a criar funções de modelo de trabalho isoladas do .NET.

Introdução Conceitos Exemplos

Para saber mais sobre como implantar um projeto de modelo de trabalho isolado no Azure, confira Implantar no Azure Functions.

Benefícios do modelo de trabalho isolado

Há dois modos nos quais você pode executar suas funções de biblioteca de classes do .NET: no mesmo processo que o runtime do host do Functions (em processo) ou em um processo de trabalho isolado. Quando suas funções .NET são executadas em um processo de trabalho isolado, você pode aproveitar os seguintes benefícios:

  • Menos conflitos: como suas funções são executadas em um processo separado, os assemblies usados em seu aplicativo não entram em conflito com versões diferentes dos mesmos assemblies usados pelo processo de host.
  • Controle total do processo: você controla a inicialização do aplicativo, o que significa que você pode gerenciar as configurações usadas e o middleware iniciado.
  • Injeção de dependência padrão: como você tem controle total do processo, você pode usar os comportamentos atuais do .NET para injeção de dependência e incorporar middleware em seu aplicativo de funções.
  • Flexibilidade de versão do .NET: em execução fora do processo de host significa que suas funções podem ser executadas em versões do .NET sem suporte nativo pelo runtime do Functions, incluindo o .NET Framework.

Se você tiver um aplicativo de funções C# existente que é executado em processo, precisará migrar seu aplicativo para aproveitar esses benefícios. Para obter mais informações, confira Migrar aplicativos .NET do modelo em processo para o modelo de trabalho isolado.

Para obter uma comparação abrangente entre os dois modos, confira Diferenças entre o modelo de processo de trabalho isolado e o modelo em processo do .NET Azure Functions.

Versões suportadas

As versões runtime do Functions dão suporte a versões específicas do .NET. Saiba mais sobre as versões do Functions, confira Visão geral de versões do Azure Functions runtime. O suporte a uma versão depende de suas funções serem executadas em processo ou no processo de trabalho isolado.

Observação

Para saber como alterar a versão de runtime do Functions usada pelo aplicativo de funções, confira Exibir e atualizar a versão de runtime atual.

A tabela a seguir mostra o nível mais alto do .NET ou .NET Framework que pode ser usado com uma versão específica do Functions.

Versão do runtime do Functions Modelo de trabalho isolado Modelo em processo5
Functions 4.x .NET 8.0
.NET 7.01
.NET 6.02
.NET Framework 4.83
.NET 6.02
Functions 1.x4 N/D .NET Framework 4.8

1 O .NET 7 chega ao fim do suporte oficial em 14 de maio de 2024.
2 O .NET 6 chega ao fim do suporte oficial em 12 de novembro de 2024.
3 O processo de build também requer o SDK do .NET. 4 O suporte termina para a versão 1.x do runtime do Azure Functions em 14 de setembro de 2026. Para obter mais informações, confira este comunicado de suporte. Para obter suporte completo contínuo, você deve migrar seus aplicativos para a versão 4.x.
5 O suporte termina para o modelo em processo em 10 de novembro de 2026. Para obter mais informações, confira este comunicado de suporte. Para obter suporte completo contínuo, você deve migrar seus aplicativos para o modelo de trabalho isolado.

Para receber as notícias mais recentes sobre as versões de Azure Functions, incluindo a remoção de versões secundárias específicas mais antigas, acompanhe os comunicados de Serviço de Aplicativo do Azure.

Estrutura do projeto

Um projeto do .NET para o Azure Functions usando o modelo de trabalho isolado é basicamente um projeto de aplicativo de console do .NET direcionado a um runtime do .NET com suporte. Estes são os arquivos básicos necessários em qualquer projeto isolado do .NET:

  • Arquivo de projeto C# (.csproj) que define o projeto e as dependências.
  • Arquivo Program. cs que é o ponto de entrada para o aplicativo.
  • Todos os arquivos de código que definem suas funções.
  • Arquivo host.json que define a configuração compartilhada por funções em seu projeto.
  • Arquivo local.settings.json que define as variáveis de ambiente usadas pelo projeto quando executadas localmente em seu computador.

Para obter exemplos completos, confira o projeto de exemplo do .NET 8 e o projeto de exemplo do .NET Framework 4.8.

Referências de pacote

Um projeto .NET para o Azure Functions usando o modelo de trabalho isolado usa um conjunto exclusivo de pacotes, tanto para funcionalidade principal quanto para extensões de associação.

Pacotes principais

Os pacotes a seguir são necessários para executar as funções do .NET em um processo de trabalho isolado:

Pacotes de extensão

Como as funções de processo de trabalho isolado do .NET usam tipos de associação diferentes, elas exigem um conjunto exclusivo de pacotes de extensão de associação.

Você encontra esses pacotes de extensão em Microsoft.Azure.Functions.Worker.Extensions.

Inicialização e configuração

Ao usar funções isoladas do .NET, você tem acesso à inicialização do seu aplicativo de funções, que geralmente está em Program.cs. Você é responsável por criar e iniciar sua própria instância de host. Assim, você também tem acesso direto ao pipeline de configuração para seu aplicativo. Com o processo de trabalho isolado das funções do .NET você pode adicionar configurações com mais facilidade, injetar dependências e executar seu middleware.

O código a seguir mostra um exemplo de um pipeline HostBuilder:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(s =>
    {
        s.AddApplicationInsightsTelemetryWorkerService();
        s.ConfigureFunctionsApplicationInsights();
        s.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
        s.Configure<LoggerFilterOptions>(options =>
        {
            // The Application Insights SDK adds a default logging filter that instructs ILogger to capture only Warning and more severe logs. Application Insights requires an explicit override.
            // Log levels can also be configured using appsettings.json. For more information, see https://learn.microsoft.com/en-us/azure/azure-monitor/app/worker-service#ilogger-logs
            LoggerFilterRule toRemove = options.Rules.FirstOrDefault(rule => rule.ProviderName
                == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");

            if (toRemove is not null)
            {
                options.Rules.Remove(toRemove);
            }
        });
    })
    .Build();

Este código requer using Microsoft.Extensions.DependencyInjection;.

Antes de chamar Build() no HostBuilder, você deve:

  • Chamar ConfigureFunctionsWebApplication() se estiver usando de integração doASP.NET Core ou, caso não esteja usando, ConfigureFunctionsWorkerDefaults(). Consulte gatilho HTTP para obter detalhes sobre essas opções.
    Se você estiver escrevendo seu aplicativo usando F#, algumas extensões de gatilho e associação exigirão uma configuração extra. Confira a documentação de configuração da extensão Blobs, da extensão Tabelas e da extensão Cosmos DB quando planejar usar essas extensões em um aplicativo F#.
  • Configure todos os serviços ou configuração de aplicativo necessários para seu projeto. Confira Configuração para obter detalhes.
    Se você estiver planejando usar o Application Insights, precisará chamar AddApplicationInsightsTelemetryWorkerService() e ConfigureFunctionsApplicationInsights() no delegado ConfigureServices(). Consulte Application Insights para obter detalhes.

Se o projeto for direcionado para o .NET Framework 4.8, você também precisará adicionar FunctionsDebugger.Enable(); antes de criar o HostBuilder. Essa deve ser a primeira linha de seu método Main(). Para mais informações, confira Depuração ao direcionar o .NET Framework.

O HostBuilder é usado para criar e retornar uma instância de IHost totalmente inicializada, que você executa de forma assíncrona para iniciar seu aplicativo de funções.

await host.RunAsync();

Configuração

O método ConfigureFunctionsWorkerDefaults é usado para adicionar as configurações necessárias para que o aplicativo de funções seja executado em um processo de trabalho isolado, o que inclui a seguinte funcionalidade:

  • Conjunto padrão de conversores.
  • Defina o JsonSerializerOptions padrão para ignorar maiúsculas e minúsculas nos nomes de propriedade.
  • Integrar com o log de Azure Functions.
  • Middleware de associação de saída e recursos.
  • Middleware de execução de função.
  • Suporte a gRPC padrão.
.ConfigureFunctionsWorkerDefaults()

Ter acesso ao pipeline do host Builder significa que você também pode definir qualquer configuração específica do aplicativo durante a inicialização. Você pode chamar o método ConfigureAppConfiguration em HostBuilder uma ou mais vezes para adicionar as configurações exigidas por seu aplicativo de funções. Para obter mais informações sobre a configuração de aplicativos, consulte Configuração no ASP.NET Core.

Essas configurações se aplicam ao seu aplicativo de funções em execução em um processo separado. Para fazer alterações no host de funções ou na configuração do gatilho e da associação, você ainda precisará usar o arquivo host.json.

Observação

As fontes de configuração personalizadas não podem ser usadas para a configuração de gatilhos e associações. A configuração de gatilho e associação deve estar disponível para a plataforma do Functions e não apenas para o código do aplicativo. Você pode fornecer essa configuração por meio das configurações do aplicativo, das referências do Key Vault ou dos recursos de referências da Configuração de Aplicativo.

Injeção de dependência

A injeção de dependência é simplificada quando comparada às funções em processo do .NET, o que exige que você crie uma classe de inicialização para registrar os serviços.

Para um aplicativo de processo isolado do .NET, use a maneira padrão do .NET de chamar ConfigureServices no construtor de host e use os métodos de extensão em IServiceCollection para injetar serviços específicos.

O exemplo a seguir injeta uma dependência de serviço singleton:

.ConfigureServices(services =>
{
    services.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
})

Este código requer using Microsoft.Extensions.DependencyInjection;. Para saber mais, confira Injeção de dependência em ASP.NET Core.

Registrar clientes do Azure

A injeção de dependência pode ser usada para interagir com outros serviços do Azure. Você pode injetar clientes do SDK do Azure para .NET usando o pacote Microsoft.Extensions.Azure . Depois de instalar o pacote, registre os clientes chamando AddAzureClients() na coleção de serviços no Program.cs. O exemplo a seguir configura um cliente nomeado para Blobs do Azure:

using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices((hostContext, services) =>
    {
        services.AddAzureClients(clientBuilder =>
        {
            clientBuilder.AddBlobServiceClient(hostContext.Configuration.GetSection("MyStorageConnection"))
                .WithName("copierOutputBlob");
        });
    })
    .Build();

host.Run();

O exemplo a seguir mostra como podemos usar esse registro e os tipos de SDK para copiar o conteúdo do blob como um fluxo de um contêiner para outro usando um cliente injetado:

using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Logging;

namespace MyFunctionApp
{
    public class BlobCopier
    {
        private readonly ILogger<BlobCopier> _logger;
        private readonly BlobContainerClient _copyContainerClient;

        public BlobCopier(ILogger<BlobCopier> logger, IAzureClientFactory<BlobServiceClient> blobClientFactory)
        {
            _logger = logger;
            _copyContainerClient = blobClientFactory.CreateClient("copierOutputBlob").GetBlobContainerClient("samples-workitems-copy");
            _copyContainerClient.CreateIfNotExists();
        }

        [Function("BlobCopier")]
        public async Task Run([BlobTrigger("samples-workitems/{name}", Connection = "MyStorageConnection")] Stream myBlob, string name)
        {
            await _copyContainerClient.UploadBlobAsync(name, myBlob);
            _logger.LogInformation($"Blob {name} copied!");
        }

    }
}

O ILogger<T> neste exemplo também foi obtido por meio da injeção de dependência, portanto, é registrado automaticamente. Para saber mais sobre as opções de configuração para registro em log, confira Registro em log.

Dica

O exemplo usou uma cadeia de caracteres literal para o nome do cliente no Program.cs e na função. Considere, em vez disso, usar uma cadeia de caracteres constante compartilhada definida na classe de função. Por exemplo, você pode adicionar public const string CopyStorageClientName = nameof(_copyContainerClient); e referenciar BlobCopier.CopyStorageClientName em ambos os locais. Da mesma forma, você pode definir o nome da seção de configuração com a função em vez de no Program.cs.

Middleware

O .NET isolado também dá suporte ao registro de middleware, novamente usando um modelo semelhante ao que existe em ASP.NET. Esse modelo oferece a capacidade de injetar a lógica no pipeline de invocação e as funções before e after são executadas.

O método de extensão ConfigureFunctionsWorkerDefaults tem uma sobrecarga que permite registrar seu próprio middleware, como você pode ver no exemplo a seguir.

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(workerApplication =>
    {
        // Register our custom middlewares with the worker

        workerApplication.UseMiddleware<ExceptionHandlingMiddleware>();

        workerApplication.UseMiddleware<MyCustomMiddleware>();

        workerApplication.UseWhen<StampHttpHeaderMiddleware>((context) =>
        {
            // We want to use this middleware only for http trigger invocations.
            return context.FunctionDefinition.InputBindings.Values
                          .First(a => a.Type.EndsWith("Trigger")).Type == "httpTrigger";
        });
    })
    .Build();

O método de extensão UseWhen pode ser usado para registrar um middleware que é executado condicionalmente. Você deve passar para esse método um predicado que retorna um valor booliano e o middleware participa do pipeline de processamento de invocação quando o valor retornado do predicado é true.

Os métodos de extensão a seguir no FunctionContext facilitam o trabalho com middleware no modelo isolado.

Método Descrição
GetHttpRequestDataAsync Obtém a instância de HttpRequestData quando chamada por um gatilho HTTP. Esse método retorna uma instância de ValueTask<HttpRequestData?>, o que é útil quando você deseja ler dados de mensagem, como cookies e cabeçalhos de solicitação.
GetHttpResponseData Obtém a instância de HttpResponseData quando chamada por um gatilho HTTP.
GetInvocationResult Obtém uma instância de InvocationResult, que representa o resultado da execução da função atual. Use a propriedade Value para obter ou definir o valor conforme necessário.
GetOutputBindings Obtém as entradas de associação de saída para a execução da função atual. Cada entrada no resultado desse método é do tipo OutputBindingData. Você pode usar a propriedade Value para obter ou definir o valor conforme necessário.
BindInputAsync Associa um item de associação de entrada para a instância de BindingMetadata solicitada. Por exemplo, você poderá usar esse método quando tiver uma função com uma associação de entrada BlobInput que precisa ser usada pelo seu middleware.

Este é um exemplo de uma implementação de middleware que lê a instância HttpRequestData e atualiza a instância HttpResponseData durante a execução da função:

internal sealed class StampHttpHeaderMiddleware : IFunctionsWorkerMiddleware
{
    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        var requestData = await context.GetHttpRequestDataAsync();

        string correlationId;
        if (requestData!.Headers.TryGetValues("x-correlationId", out var values))
        {
            correlationId = values.First();
        }
        else
        {
            correlationId = Guid.NewGuid().ToString();
        }

        await next(context);

        context.GetHttpResponseData()?.Headers.Add("x-correlationId", correlationId);
    }
}

Esse middleware verifica a presença de um cabeçalho de solicitação específico (x-correlationId) e, quando presente, usa o valor do cabeçalho para carimbar um cabeçalho de resposta. Caso contrário, ele gera um novo valor de GUID e o utiliza para carimbar o cabeçalho de resposta. Para obter um exemplo mais completo de como usar o middleware personalizado em seu aplicativo de funções, consulte o exemplo de referência de middleware personalizado.

Personalizando a serialização JSON

O modelo de trabalho isolado usa System.Text.Json por padrão. Você pode personalizar o comportamento do serializador configurando serviços como parte do arquivo Program.cs. O exemplo a seguir mostra esse uso ConfigureFunctionsWebApplication, mas também funcionará para ConfigureFunctionsWorkerDefaults:

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication((IFunctionsWorkerApplicationBuilder builder) =>
    {
        builder.Services.Configure<JsonSerializerOptions>(jsonSerializerOptions =>
        {
            jsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
            jsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;

            // override the default value
            jsonSerializerOptions.PropertyNameCaseInsensitive = false;
        });
    })
    .Build();

Em vez disso, convém usar JSON.NET (Newtonsoft.Json) para serialização. Para fazer isso, você instalaria o pacote Microsoft.Azure.Core.NewtonsoftJson. Em seguida, no registro do serviço, você reatribuiria a propriedade Serializer na configuração WorkerOptions. O exemplo a seguir mostra esse uso ConfigureFunctionsWebApplication, mas também funcionará para ConfigureFunctionsWorkerDefaults:

var host = new HostBuilder()
    .ConfigureFunctionsWebApplication((IFunctionsWorkerApplicationBuilder builder) =>
    {
        builder.Services.Configure<WorkerOptions>(workerOptions =>
        {
            var settings = NewtonsoftJsonObjectSerializer.CreateJsonSerializerSettings();
            settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            settings.NullValueHandling = NullValueHandling.Ignore;

            workerOptions.Serializer = new NewtonsoftJsonObjectSerializer(settings);
        });
    })
    .Build();

Métodos reconhecidos como funções

Um método de função é um método público de uma classe pública com um atributo Function aplicado ao método e um atributo disparo aplicado a um parâmetro de entrada, conforme mostrado no exemplo a seguir:

[Function(nameof(QueueFunction))]
[QueueOutput("output-queue")]
public string[] Run([QueueTrigger("input-queue")] Album myQueueItem, FunctionContext context)

O atributo de gatilho especifica o tipo de gatilho e associa dados de entrada a um parâmetro de método. A função de exemplo é disparada por uma mensagem de fila, a qual é transmitida para o método no parâmetro myQueueItem.

O atributo Function marca o método como um ponto de entrada da função. O nome deve ser exclusivo dentro de um projeto, começar com uma letra e conter apenas letras, números, _ e -, até 127 caracteres. Modelos de projeto geralmente criam um método chamado Run, mas o nome do método pode ser qualquer nome de método C# válido. O método deve ser um membro público de uma classe pública. Em geral, deve ser um método de instância para que os serviços possam ser transmitidos via injeção de dependência.

Parâmetros de função

Aqui estão alguns dos parâmetros que você pode incluir como parte de uma assinatura de método de função:

Contexto de execução

O .NET isolado passa um objeto FunctionContext para seus métodos de função. Esse objeto permite que você obtenha uma instância de ILogger para gravar nos logs chamando o método GetLogger e fornecendo uma categoryName cadeia de caracteres. Você pode usar esse contexto para obter um ILogger sem precisar usar a injeção de dependência. Para saber mais, consulte Logging.

Tokens de cancelamento

Uma função pode aceitar um parâmetro CancellationToken que permite ao sistema operacional notificar seu código quando a função está prestes a ser encerrada. Você pode usar essa notificação para certificar-se de que a função não finalize inesperadamente de uma maneira que os dados fiquem em um estado inconsistente.

Há suporte a tokens de cancelamento em funções .NET quando executados em um processo de trabalho isolado. O exemplo a seguir gera uma exceção quando uma solicitação de cancelamento é recebida:

[Function(nameof(ThrowOnCancellation))]
public async Task ThrowOnCancellation(
    [EventHubTrigger("sample-workitem-1", Connection = "EventHubConnection")] string[] messages,
    FunctionContext context,
    CancellationToken cancellationToken)
{
    _logger.LogInformation("C# EventHub {functionName} trigger function processing a request.", nameof(ThrowOnCancellation));

    foreach (var message in messages)
    {
        cancellationToken.ThrowIfCancellationRequested();
        await Task.Delay(6000); // task delay to simulate message processing
        _logger.LogInformation("Message '{msg}' was processed.", message);
    }
}

O exemplo a seguir executa ações de limpeza quando uma solicitação de cancelamento é recebida:

[Function(nameof(HandleCancellationCleanup))]
public async Task HandleCancellationCleanup(
    [EventHubTrigger("sample-workitem-2", Connection = "EventHubConnection")] string[] messages,
    FunctionContext context,
    CancellationToken cancellationToken)
{
    _logger.LogInformation("C# EventHub {functionName} trigger function processing a request.", nameof(HandleCancellationCleanup));

    foreach (var message in messages)
    {
        if (cancellationToken.IsCancellationRequested)
        {
            _logger.LogInformation("A cancellation token was received, taking precautionary actions.");
            // Take precautions like noting how far along you are with processing the batch
            _logger.LogInformation("Precautionary activities complete.");
            break;
        }

        await Task.Delay(6000); // task delay to simulate message processing
        _logger.LogInformation("Message '{msg}' was processed.", message);
    }
}

Associações

As associações são definidas usando atributos em métodos, parâmetros e tipos de retorno. As associações podem fornecer dados como cadeias de caracteres, matrizes e tipos serializáveis, como POCOs (objetos de classe antiga simples). Para algumas extensões de associação, você também pode associar a tipos específicos do serviço definidos em SDKs de serviço.

Para os gatilhos HTTP, confira a seção Gatilho HTTP.

Para obter um conjunto completo de exemplos de referência usando gatilhos e associações com as funções do processo de trabalho isoladas, confira o exemplo de referência de extensões de associação.

Associações de entrada

Uma função pode ter zero ou mais associações de entrada que podem passar dados para uma função. Como gatilhos, as associações de entrada são definidas pela aplicação de um atributo de associação a um parâmetro de entrada. Quando a função é executada, o tempo de execução tenta obter os dados especificados na associação. Os dados que estão sendo solicitados geralmente dependem das informações fornecidas pelo gatilho usando parâmetros de associação.

Associações de saída

Para gravar em uma associação de saída, você deve aplicar um atributo de associação de saída ao método de função, que define como gravar no serviço associado. O valor retornado pelo método é gravado na associação de saída. Por exemplo, o exemplo a seguir grava um valor de cadeia de caracteres em uma fila de mensagens chamada output-queue usando uma associação de saída:

[Function(nameof(QueueFunction))]
[QueueOutput("output-queue")]
public string[] Run([QueueTrigger("input-queue")] Album myQueueItem, FunctionContext context)
{
    // Use a string array to return more than one message.
    string[] messages = {
        $"Album name = {myQueueItem.Name}",
        $"Album songs = {myQueueItem.Songs.ToString()}"};

    _logger.LogInformation("{msg1},{msg2}", messages[0], messages[1]);

    // Queue Output messages
    return messages;
}

Várias associações de saída

Os dados gravados em uma associação de saída são sempre o valor de retorno da função. Se você precisar gravar em mais de uma associação de saída, deverá criar um tipo de retorno personalizado. Esse tipo de retorno deve ter o atributo de associação de saída aplicado a uma ou mais propriedades da classe. O exemplo a seguir de um gatilho HTTP é gravado na resposta HTTP e em uma associação de saída de fila:

public static class MultiOutput
{
    [Function(nameof(MultiOutput))]
    public static MyOutputType Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
        FunctionContext context)
    {
        var response = req.CreateResponse(HttpStatusCode.OK);
        response.WriteString("Success!");

        string myQueueOutput = "some output";

        return new MyOutputType()
        {
            Name = myQueueOutput,
            HttpResponse = response
        };
    }
}

public class MyOutputType
{
    [QueueOutput("myQueue")]
    public string Name { get; set; }

    public HttpResponseData HttpResponse { get; set; }
}

A resposta de um gatilho HTTP é sempre considerada uma saída, portanto, um atributo de valor de retorno não é necessário.

Tipos de SDK

Para alguns tipos de associação específicos do serviço, os dados de associação podem ser fornecidos usando tipos de SDKs e estruturas de serviço. Eles fornecem mais recursos além do que uma cadeia de caracteres serializada ou um POCO (objeto CRL básico) pode oferecer. Para usar os tipos mais recentes, seu projeto precisa ser atualizado para usar versões mais recentes de dependências principais.

Dependência Requisito de versão
Microsoft.Azure.Functions.Worker 1.18.0 ou posterior
Microsoft.Azure.Functions.Worker.Sdk 1.13.0 ou posterior

Ao testar tipos de SDK localmente em seu computador, você também precisará usar o Azure Functions Core Tools, versão 4.0.5000 ou posterior. Você pode verificar sua versão atual usando o comando func version.

Cada extensão de gatilho e associação também tem seu próprio requisito de versão mínima, que é descrito nos artigos de referência de extensão. As seguintes associações específicas do serviço fornecem os tipos de SDK:

Serviço Gatilho Associação de entrada Associação de saída
Blobs do Azure Disponível para o público geral Disponível para o público geral Os tipos de SDK não são recomendados.1
Filas do Azure Disponível para o público geral A associação de entrada não existe Os tipos de SDK não são recomendados.1
Barramento de Serviço do Azure Disponível para o público geral A associação de entrada não existe Os tipos de SDK não são recomendados.1
Hubs de eventos do Azure Disponível para o público geral A associação de entrada não existe Os tipos de SDK não são recomendados.1
Azure Cosmos DB Tipos de SDK não usados2 Disponível para o público geral Os tipos de SDK não são recomendados.1
Tabelas do Azure O gatilho não existe Disponível para o público geral Os tipos de SDK não são recomendados.1
Grade de Eventos do Azure Disponível para o público geral A associação de entrada não existe Os tipos de SDK não são recomendados.1

1 Para cenários de saída nos quais você usaria um tipo de SDK, você deve criar e trabalhar com clientes do SDK diretamente em vez de usar uma associação de saída. Confira Registrar clientes do Azure para obter um exemplo de injeção de dependência.

2 O gatilho do Cosmos DB usa o feed de alterações do Azure Cosmos DB e expõe itens do feed de alterações como tipos serializáveis em JSON. A ausência de tipos de SDK é por design para esse cenário.

Observação

Ao usar expressões de associação que dependem de dados de gatilho, não há suporte para tipos de SDK para o gatilho em si.

Gatilho HTTP

Os gatilhos HTTP permitem que uma função seja invocada por uma solicitação HTTP. Há duas abordagens diferentes que podem ser usadas:

  • Um modelo de integração ASP.NET Core que usa conceitos familiares aos desenvolvedores ASP.NET Core
  • Um modelo interno, que não requer dependências extras e usa tipos personalizados para solicitações e respostas HTTP. Essa abordagem é mantida para compatibilidade com versões anteriores com aplicativos de trabalho isolados do .NET anteriores.

Integração ASP.NET Core

Esta seção mostra como trabalhar com a solicitação HTTP subjacente e os objetos de resposta usando tipos de ASP.NET Core incluindo HttpRequest, HttpResponse e IActionResult. Esse modelo não está disponível para aplicativos direcionados ao .NET Framework, que devem usar o modelo interno.

Observação

Nem todos os recursos do ASP.NET Core são expostos por esse modelo. Especificamente, o pipeline de middleware ASP.NET Core e as funcionalidades de roteamento não estão disponíveis. A integração do ASP.NET Core exige que você use pacotes atualizados.

Para habilitar a integração do ASP.NET Core para HTTP:

  1. Adicione uma referência em seu projeto ao pacote Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore, versão 1.0.0 ou posterior.

  2. Atualize seu projeto para usar estas versões de pacote específicas:

  3. No seu arquivo Program.cs, atualize a configuração do criador de host para usar ConfigureFunctionsWebApplication() em vez de ConfigureFunctionsWorkerDefaults(). O exemplo a seguir mostra uma configuração mínima sem outras personalizações:

    using Microsoft.Extensions.Hosting;
    using Microsoft.Azure.Functions.Worker;
    
    var host = new HostBuilder()
        .ConfigureFunctionsWebApplication()
        .Build();
    
    host.Run();
    
  4. Atualize as funções disparadas por HTTP existentes para usar os tipos de ASP.NET Core. Este exemplo mostra o HttpRequest padrão e um IActionResult usado para uma função simples "olá, mundo":

    [Function("HttpFunction")]
    public IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req)
    {
        return new OkObjectResult($"Welcome to Azure Functions, {req.Query["name"]}!");
    }
    

Modelo HTTP interno

No modelo interno, o sistema converte a mensagem de solicitação HTTP recebida em um objetoHttpRequestData que é passado para a função. Esse objeto fornece dados da solicitação, incluindo Headers, Cookies, Identities, URL e uma mensagem opcional Body. Esse objeto é uma representação da solicitação HTTP, porém, não tem conexão direta com o ouvinte HTTP subjacente ou à mensagem recebida.

Da mesma forma, a função retorna um objeto HttpResponseData, que fornece dados usados para criar a resposta HTTP, incluindo a mensagem StatusCode, Headers e, opcionalmente, uma mensagem Body.

O exemplo a seguir demonstra o uso HttpRequestData e HttpResponseData:

[Function(nameof(HttpFunction))]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req,
    FunctionContext executionContext)
{
    var logger = executionContext.GetLogger(nameof(HttpFunction));
    logger.LogInformation("message logged");

    var response = req.CreateResponse(HttpStatusCode.OK);
    response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
    response.WriteString("Welcome to .NET isolated worker !!");

    return response;
}

Registrando em log

No .NET isolado, você pode gravar em logs usando uma instância ILogger<T> ou ILogger. O agente pode ser obtido por meio da injeção de dependência de um ILogger<T> ou de um [ILoggerFactory]:

public class MyFunction {
    
    private readonly ILogger<MyFunction> _logger;
    
    public MyFunction(ILogger<MyFunction> logger) {
        _logger = logger;
    }
    
    [Function(nameof(MyFunction))]
    public void Run([BlobTrigger("samples-workitems/{name}", Connection = "")] string myBlob, string name)
    {
        _logger.LogInformation($"C# Blob trigger function Processed blob\n Name: {name} \n Data: {myBlob}");
    }

}

O agente também pode ser obtido de um objeto FunctionContext passado para sua função. Chame o método GetLogger<T> ou GetLogger, passando um valor de cadeia de caracteres que é o nome da categoria na qual os logs são gravados. A categoria geralmente é o nome da função específica da qual os logs são gravados. Para saber mais sobre categorias, consulte o artigo monitoramento.

Use os métodos de ILogger<T> e ILogger para gravar vários níveis de registro, como LogWarning ou LogError. Para saber mais sobre os níveis de log, consulte o artigo monitoramento. Você pode personalizar os níveis de log para componentes adicionados ao seu código registrando filtros como parte da configuração HostBuilder:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services =>
    {
        // Registers IHttpClientFactory.
        // By default this sends a lot of Information-level logs.
        services.AddHttpClient();
    })
    .ConfigureLogging(logging =>
    {
        // Disable IHttpClientFactory Informational logs.
        // Note -- you can also remove the handler that does the logging: https://github.com/aspnet/HttpClientFactory/issues/196#issuecomment-432755765 
        logging.AddFilter("System.Net.Http.HttpClient", LogLevel.Warning);
    })
    .Build();

Como parte da configuração do seu aplicativo em Program.cs, você também pode definir o comportamento de como os erros são exibidos em seus registros. Por padrão, as exceções lançadas pelo seu código podem acabar encapsuladas em um RpcException. Para remover essa camada extra, defina a propriedade EnableUserCodeException como "true" como parte da configuração do construtor:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder => {}, options =>
    {
        options.EnableUserCodeException = true;
    })
    .Build();

Application Insights

Você pode configurar seu aplicativo de processo isolado para emitir logs diretamente no Application Insights. Esse comportamento substitui o comportamento padrão de retransmissão de logs por meio do host e é recomendado porque fornece controle sobre como esses logs são emitidos.

Instalar Pacotes

Para gravar logs diretamente no Application Insights do seu código, adicione referências a estes pacotes em seu projeto:

Você pode executar os seguintes comandos para adicionar essas referências ao seu projeto:

dotnet add package Microsoft.ApplicationInsights.WorkerService
dotnet add package Microsoft.Azure.Functions.Worker.ApplicationInsights

Configurar inicialização

Com os pacotes instalados, você deve chamar AddApplicationInsightsTelemetryWorkerService() e ConfigureFunctionsApplicationInsights() durante a configuração de serviço em seu arquivo Program.cs, como neste exemplo:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
    
var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services => {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .Build();

host.Run();

A chamada para ConfigureFunctionsApplicationInsights() adiciona um ITelemetryModule, que escuta um ActivitySource definido pelo Functions. Isso cria a telemetria de dependência necessária para dar suporte ao rastreamento distribuído. Para saber mais sobre AddApplicationInsightsTelemetryWorkerService() e como usá-lo, consulte Application Insights para aplicativos do Serviço de Trabalho.

Gerenciar níveis de log

Importante

O host do Functions e o trabalho do processo isolado têm configurações separadas para níveis de log, etc. Qualquer configuração do Application Insights no host.json não afetará o registro em log do trabalho e, da mesma forma, a configuração feita no código do trabalho não afetará o registro em log do host. Será necessário aplicar alterações em ambos os locais se o cenário exigir personalização em ambas as camadas.

O restante do seu aplicativo continua funcionando com ILogger e ILogger<T>. No entanto, por padrão, o SDK do Application Insights adiciona um filtro de registro que instrui o agente a capturar apenas os avisos e registros mais graves. Se você quiser desabilitar esse comportamento, remova a regra de filtro como parte da configuração do serviço:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services => {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
    })
    .ConfigureLogging(logging =>
    {
        logging.Services.Configure<LoggerFilterOptions>(options =>
        {
            LoggerFilterRule defaultRule = options.Rules.FirstOrDefault(rule => rule.ProviderName
                == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");
            if (defaultRule is not null)
            {
                options.Rules.Remove(defaultRule);
            }
        });
    })
    .Build();

host.Run();

Otimizações de desempenho

Esta seção descreve as opções que podem ser habilitadas para melhorar o desempenho da inicialização a frio.

Em geral, seu aplicativo deve usar as versões mais recentes de suas dependências principais. No mínimo, você deve atualizar seu projeto da seguinte maneira:

  1. Atualize Microsoft.Azure.Functions.Worker para a versão 1.19.0 ou posterior.
  2. Atualize Microsoft.Azure.Functions.Worker.Sdk para a versão 1.16.4 ou posterior.
  3. Adicione uma referência de estrutura a Microsoft.AspNetCore.App, a menos que seu aplicativo seja direcionado ao .NET Framework.

O snippet a seguir mostra essa configuração no contexto de um arquivo de projeto:

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
    <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.4" />
  </ItemGroup>

Espaços reservados

Espaços reservados são uma funcionalidade de plataforma que melhora a inicialização a frio para aplicativos direcionados ao .NET 6 ou posterior. Para usar essa otimização, você deve habilitar explicitamente os espaços reservados usando estas etapas:

  1. Atualize a configuração do projeto para usar as versões de dependência mais recentes, conforme detalhado na seção anterior.

  2. Defina a configuração de aplicativo WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED como 1, o que você pode fazer usando este comando az functionapp config appsettings set:

    az functionapp config appsettings set -g <groupName> -n <appName> --settings 'WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED=1'
    

    Neste exemplo, substitua <groupName> pelo nome do grupo de recursos e substitua <appName> pelo nome do aplicativo de funções.

  3. Certifique-se de que a propriedade netFrameworkVersion do aplicativo de funções corresponde à estrutura de destino do projeto, que deve ser .NET 6 ou posterior. Você pode fazer isso usando este comando az functionapp config set:

    az functionapp config set -g <groupName> -n <appName> --net-framework-version <framework>
    

    Nesse exemplo, substitua também <framework> pela cadeia de caracteres de versão apropriada, como v8.0, v7.0 ou v6.0, de acordo com a versão do .NET de destino.

  4. Certifique-se de que o aplicativo de funções está configurado para usar um processo de 64 bits, o que você pode fazer usando este comando az functionapp config set:

    az functionapp config set -g <groupName> -n <appName> --use-32bit-worker-process false
    

Importante

Ao definir o WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED como 1, todas as outras configurações do aplicativo de funções devem ser definidas corretamente. Caso contrário, seu aplicativo de funções poderá falhar ao iniciar.

Executor otimizado

O executor de função é um componente da plataforma que faz com que as invocações sejam executadas. Uma versão otimizada desse componente é habilitada por padrão, começando com a versão 1.16.2 do SDK. Nenhuma configuração adicional é necessária.

ReadyToRun

Você pode compilar seu aplicativo de funções como binários ReadyToRun. ReadyToRun é uma forma de compilação antecipada que pode melhorar o desempenho de inicialização, para ajudar a reduzir o efeito de inicializações a frio durante a execução em um plano de Consumo. O ReadyToRun está disponível no .NET 6 e requer a versão 4.0 ou posterior do runtime do Azure Functions.

ReadyToRun exige que você compile o projeto em relação à arquitetura de runtime do aplicativo de hospedagem. Se eles não estiverem alinhados, seu aplicativo encontrará um erro na inicialização. Selecione o identificador de runtime nesta tabela:

Sistema operacional O aplicativo é de 32 bits1 Identificador de runtime
Windows Verdadeiro win-x86
Windows Falso win-x64
Linux Verdadeiro N/D (sem suporte)
Linux Falso linux-x64

1 Somente aplicativos de 64 bits são elegíveis para algumas outras otimizações de desempenho.

Para verificar se o aplicativo do Windows é de 32 ou 64 bits, você pode executar o comando da CLI a seguir, substituindo <group_name> pelo nome do grupo de recursos e <app_name> pelo nome do aplicativo. Uma saída de "true" indica que o aplicativo é de 32 bits e "false" indica 64 bits.

 az functionapp config show -g <group_name> -n <app_name> --query "use32BitWorkerProcess"

Você pode alterar seu aplicativo para 64 bits com o seguinte comando, usando as mesmas substituições:

az functionapp config set -g <group_name> -n <app_name> --use-32bit-worker-process false`

Para compilar seu projeto como ReadyToRun, atualize o arquivo de projeto adicionando os elementos <PublishReadyToRun> e <RuntimeIdentifier>. O exemplo a seguir mostra uma configuração para publicação em um aplicativo de funções de 64 bits do Windows.

<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
  <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  <PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

Se você não quiser definir o <RuntimeIdentifier> como parte do arquivo de projeto, também poderá configurá-lo como parte do próprio processo de publicação. Por exemplo, com um aplicativo de funções de 64 bits do Windows, o comando da CLI do .NET será:

dotnet publish --runtime win-x64

No Visual Studio, a opção Runtime de Destino no perfil de publicação deve ser definida como o identificador de runtime correto. Quando definido como o valor padrão de Portátil, ReadyToRun não será usado.

Implantar no Azure Functions

Quando você implanta seu projeto de código de função no Azure, ele deve ser executado em um aplicativo de funções ou em um contêiner do Linux. O aplicativo de funções e outros recursos necessários do Azure devem existir antes de você implantar seu código.

Você também pode implantar seu aplicativo de funções em um contêiner do Linux. Para obter mais informações, consulte Trabalhar com contêineres e Azure Functions.

Criar recursos do Azure

Você pode criar seu aplicativo de funções e outros recursos necessários no Azure usando um destes métodos:

  • Visual Studio: o Visual Studio pode criar recursos para você durante o processo de publicação do código.
  • Visual Studio Code: o Visual Studio Code pode se conectar à sua assinatura, criar os recursos necessários para seu aplicativo e publicar seu código.
  • CLI do Azure: você pode usar a CLI do Azure para criar os recursos necessários no Azure.
  • Azure PowerShell: você pode usar o Azure PowerShell para criar os recursos necessários no Azure.
  • Modelos de implantação: você pode usar modelos do ARM e arquivos Bicep para automatizar a implantação dos recursos necessários no Azure. Certifique-se de que o modelo inclui as configurações necessárias.
  • Portal do Azure: você pode criar os recursos necessários no portal do Azure.

Publicar projeto de código

Depois de criar seu aplicativo de funções e outros recursos necessários no Azure, você poderá implantar o projeto de código no Azure usando um destes métodos:

Para obter mais informações, confira Tecnologias de implantação do Azure Functions.

Requisitos para implantação

Há alguns requisitos para executar funções .NET no modelo de trabalho isolado no Azure, dependendo do sistema operacional:

Ao criar seu aplicativo de funções no Azure usando os métodos na seção anterior, essas configurações necessárias serão adicionadas para você. Ao criar esses recursos usando modelos do ARM ou arquivos Bicep para automação, você deverá defini-los no modelo.

Depuração

Ao executar localmente usando o Visual Studio ou o Visual Studio Code, você poderá depurar seu projeto de trabalho isolado do .NET normalmente. No entanto, há dois cenários de depuração que não funcionam conforme o esperado.

Depuração remota usando o Visual Studio

Como seu aplicativo de processo de trabalho isolado é executado fora do runtime do Functions, você precisa anexar o depurador remoto a um processo separado. Para saber mais sobre a depuração usando o Visual Studio, confira Depuração Remota.

Depuração ao direcionar o .NET Framework

Se o projeto isolado for direcionado ao .NET Framework 4.8, o escopo de visualização atual exigirá etapas manuais para habilitar a depuração. Essas etapas não serão necessárias se outra estrutura de destino estiver sendo usada.

Seu aplicativo deve começar com uma chamada para FunctionsDebugger.Enable(); como a primeira operação. Isso ocorre no método Main() antes de inicializar um HostBuilder. Seu arquivo Program.cs deve ser semelhante a este:

using System;
using System.Diagnostics;
using Microsoft.Extensions.Hosting;
using Microsoft.Azure.Functions.Worker;
using NetFxWorker;

namespace MyDotnetFrameworkProject
{
    internal class Program
    {
        static void Main(string[] args)
        {
            FunctionsDebugger.Enable();

            var host = new HostBuilder()
                .ConfigureFunctionsWorkerDefaults()
                .Build();

            host.Run();
        }
    }
}

Em seguida, você precisa anexar manualmente ao processo usando um depurador de .NET Framework. O Visual Studio ainda não faz isso automaticamente para aplicativos .NET Framework de processo de trabalho isolado, e a operação "Iniciar Depuração" deve ser evitada.

No diretório do projeto (ou no diretório de saída de build), execute:

func host start --dotnet-isolated-debug

Isso inicia o trabalho e o processo é interrompido com a seguinte mensagem:

Azure Functions .NET Worker (PID: <process id>) initialized in debug mode. Waiting for debugger to attach...

Em que <process id> é a ID do processo de trabalho. Agora, você pode usar Visual Studio para anexar manualmente ao processo. Para obter instruções sobre essa operação, consulte Como anexar a um processo em execução.

Depois que o depurador for anexado, a execução do processo é retomada e você poderá depurar.

Visualizar versões do .NET

Antes do lançamento para o público geral, uma versão do .NET pode ser lançada em um estado Versão prévia ou Entrar em funcionamento. Confira a Política de suporte oficial do .NET para obter detalhes sobre esses estados.

Embora seja possível direcionar uma versão específica a partir de um projeto local do Functions, os aplicativos de funções hospedados no Azure podem não ter essa versão disponível. O Azure Functions só pode ser usado com versões de Versão prévia ou Entrar em funcionamento mencionadas nesta seção.

No momento, o Azure Functions não é compatível com nenhuma versão de "Versão prévia" ou "Entrar em funcionamento" do .NET. Confira as Versões com suporte para obter uma lista de versões em disponibilidade geral que você pode usar.

Como usar um SDK do .NET de versão prévia

Para usar o Azure Functions com uma versão prévia do .NET, você precisa atualizar seu projeto:

  1. Como instalar a versão relevante do SDK do .NET no desenvolvimento
  2. Como alterar a configuração TargetFramework no arquivo .csproj

Ao fazer a implantação em um aplicativo de funções no Azure, você também precisa garantir que a estrutura seja disponibilizada para o aplicativo. Para fazer isso no Windows, use o comando da CLI a seguir. Substitua <groupName> pelo nome do grupo de recursos e substitua <appName> pelo nome do aplicativo de funções. Substitua <framework> pela cadeia de caracteres de versão apropriada, como v8.0.

az functionapp config set -g <groupName> -n <appName> --net-framework-version <framework>

Considerações sobre o uso de versões prévias do .NET

Tenha essas considerações em mente ao usar o Functions com as versões prévias do .NET:

  • Ao criar suas funções no Visual Studio, você precisará usar o Visual Studio Preview, que dá suporte à criação de projetos do Azure Functions com os SDKs de versão prévia do .NET.

  • Certifique-se de que tem as ferramentas e modelos mais recentes do Functions. Para atualizar suas ferramentas:

    1. Navegue até Ferramentas>Opções, escolha Azure Functions em Projetos e Soluções.
    2. Selecione Verificar se há atualizações e instale atualizações conforme solicitado.
  • Durante um período de versão prévia, seu ambiente de desenvolvimento pode ter uma versão mais recente da versão prévia do .NET do que o serviço hospedado. Isso pode fazer com que o aplicativo de funções falhe quando implantado. Para resolver isso, especifique a versão do SDK a ser usada no global.json.

    1. Execute o comando dotnet --list-sdks e observe a versão da versão prévia que você está usando no momento durante o desenvolvimento local.
    2. Execute o comando dotnet new globaljson --sdk-version <SDK_VERSION> --force, em que <SDK_VERSION> é a versão que você está usando localmente. Por exemplo, dotnet new globaljson --sdk-version dotnet-sdk-8.0.100-preview.7.23376.3 --force faz com que o sistema use o SDK do .NET 8 Versão Prévia 7 ao criar o projeto.

Observação

Devido ao carregamento just-in-time das estruturas de versão prévia, os aplicativos de funções executados no Windows podem apresentar tempos de inicialização a frio maiores em comparação com as versões anteriores do GA.

Próximas etapas