Guia para executar funções em .NET 5.0 em Azure

Este artigo é uma introdução à utilização de C# para desenvolver funções de processo isolado .NET, que estão a ficar sem processo em Funções Azure. O processo esgotado permite-lhe dissociar o seu código de função do tempo de funcionamento das Funções Azure. Também fornece uma forma de criar e executar funções que visam a versão atual .NET 5.0.

Introdução Conceitos Amostras

Se não precisar de suportar .NET 5.0 ou executar as suas funções fora do processo, poderá, em vez disso, desenvolver funções de biblioteca de classe C#.

Por que.PROCESSO isolado DA NET?

Anteriormente, a Azure Functions apenas apoiou um modo fortemente integrado para as funções .NET, que funcionam como uma biblioteca de classes no mesmo processo que o anfitrião. Este modo proporciona uma integração profunda entre o processo de anfitrião e as funções. Por exemplo, as funções de biblioteca de classe .NET podem partilhar APIs e tipos de ligação. No entanto, esta integração também requer um acoplamento mais apertado entre o processo de anfitrião e a função .NET. Por exemplo, as funções .NET em execução em processo são necessárias para funcionar na mesma versão de .NET que o tempo de execução das Funções. Para que possa correr fora destes constrangimentos, pode agora optar por correr num processo isolado. Este isolamento de processo também permite desenvolver funções que utilizem as atuais versões .NET (como .NET 5.0), não suportadas de forma nativa pelo tempo de funcionamento das Funções.

Como estas funções são executadas num processo separado, existem algumas diferenças de funcionalidades e funcionalidades entre aplicações de função isoladas .NET e aplicações de função de biblioteca de classe .NET.

Benefícios do esgotar do processo

Ao esgotar o processo, as suas funções .NET podem tirar partido dos seguintes benefícios:

  • Menos conflitos: como as funções funcionam num processo separado, os conjuntos utilizados na sua app não entrarão em conflito com diferentes versões dos mesmos conjuntos utilizados pelo processo de anfitrião.
  • Controlo total do processo: controla o arranque da app e pode controlar as configurações utilizadas e o middleware iniciado.
  • Injeção de dependência: como tem o controlo total do processo, pode utilizar comportamentos atuais .NET para injeção de dependência e incorporar middleware na sua aplicação de função.

Versões suportadas

As versões do tempo de execução das Funções funcionam com versões específicas de .NET. Para saber mais sobre as versões Funções, consulte as versões de tempo de execução do Azure Functions. O suporte da versão depende se as suas funções são executadas dentro ou fora do processo (isoladas).

A tabela a seguir mostra o nível mais alto de .NET Core ou .NET Framework que podem ser utilizados com uma versão específica de Funções.

Versão de tempo de execução de funções Em processo
(.biblioteca classe NET)
Fora de processo
(.NET Isolado)
Funções 4.x (Pré-visualização) .NET 6.0 (pré-visualização) .NET 6.0 (pré-visualização)2
Funções 3.x .NET Core 3.1 .NET 5.0
Funções 2.x .NET Core 2.11 n/a
Funções 1.x .NET Framework 4.8 n/a

1 Para mais detalhes, consulte as considerações de Funções v2.x.
2 Atualmente, só é possível criar funções de processo isoladas utilizando as Ferramentas Centrais de Funções Azure. Para saber mais, consulte Quickstart: Crie uma função C# em Azure a partir da linha de comando.

Para as últimas notícias sobre os lançamentos do Azure Functions, incluindo a remoção de versões menores específicas, monitorize os anúncios do Azure App Service.

.NET projeto isolado

Um projeto de função isolada .NET é basicamente um projeto de aplicação de consola .NET que visa .NET 5.0. Seguem-se os ficheiros básicos necessários em qualquer projeto isolado .NET:

Referências do pacote

Ao esgotar-se, o seu projeto .NET utiliza um conjunto único de pacotes, que implementam tanto a funcionalidade principal como as extensões de ligação.

Pacotes centrais

As seguintes embalagens são necessárias para executar as suas funções .NET num processo isolado:

Pacotes de extensão

Porque as funções que funcionam num processo isolado .NET utilizam diferentes tipos de encadernação, requerem um conjunto único de pacotes de extensão de ligação.

Encontrará estes pacotes de extensão ao abrigo do Microsoft.Azure.Functions.Worker.Extensions.

Arranque e configuração

Ao utilizar funções isoladas .NET, tem acesso ao arranque da sua aplicação de função, que normalmente está no Programa.cs. É responsável por criar e começar o seu próprio caso de anfitrião. Como tal, também tem acesso direto ao pipeline de configuração da sua aplicação. Ao esgotar o processo, pode adicionar configurações, injetar dependências e executar o seu próprio middleware.

O seguinte código mostra um exemplo de um oleoduto [HostBuilder:]

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(s =>
    {
        s.AddSingleton<IHttpResponderService, DefaultHttpResponderService>();
    })
    .Build();

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

Um HostBuilder é usado para construir e devolver uma instância IHost totalmente inicializada, que você corre assíncronamente para iniciar a sua aplicação de função.

await host.RunAsync();

Configuração

O método ConfigureFunctionsWorkerDefaults é utilizado para adicionar as definições necessárias para que a aplicação de função fique sem processo, o que inclui a seguinte funcionalidade:

  • Conjunto padrão de conversores.
  • Desa estade as [Opições JsonSerializer] padrão para ignorar o invólucro nos nomes de propriedade.
  • Integre-se com o registo de funções Azure.
  • Middleware e funcionalidades de ligação de saída.
  • Função execução middleware.
  • Suporte gRPC predefinido.
.ConfigureFunctionsWorkerDefaults()

Ter acesso ao pipeline do construtor anfitrião significa que também pode definir quaisquer configurações específicas da aplicação durante a inicialização. Pode ligar para o método [ConfigurarAppConfiguration] no HostBuilder uma ou mais vezes para adicionar as configurações necessárias pela sua aplicação de função. Para saber mais sobre a configuração da aplicação, consulte a Configuração em ASP.NET Core.

Estas configurações aplicam-se à aplicação da sua função em execução num processo separado. Para escamar as alterações no anfitrião das funções ou configuração de gatilho e de ligação, ainda terá de utilizar a host.jsno ficheiro.

Injeção de dependência

A injeção de dependência é simplificada, em comparação com as bibliotecas de classe .NET. Em vez de ter de criar uma classe de startups para registar serviços, basta ligar para a ConfigureServices no construtor anfitrião e utilizar os métodos de extensão no IServiceCollection para injetar serviços específicos.

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

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

Este código requer using Microsoft.Extensions.DependencyInjection; . Para saber mais, consulte a injeção de Dependência em ASP.NET Core.

Middleware

.NET isolado também suporta o registo de middleware, mais uma vez usando um modelo semelhante ao que existe em ASP.NET. Este modelo dá-lhe a capacidade de injetar lógica no pipeline de invocação, e antes e depois das funções executadas.

O método de extensão ConfigureFunctionsWorkerDefaults tem uma sobrecarga que permite registar o seu próprio middleware, como pode ver no exemplo seguinte.

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(workerApplication =>
    {
        // Register our custom middleware with the worker
        workerApplication.UseMiddleware<MyCustomMiddleware>();
    })
    .Build();

Para obter um exemplo mais completo de utilização de middleware personalizado na sua aplicação de função, consulte a amostra de referência de middleware personalizada.

Contexto de execução

.NET isolado passa um objeto [FunctionContext] para os seus métodos de função. Este objeto permite-lhe obter uma instância ILogger para escrever para os registos, chamando o método GetLogger e fornecendo uma categoryName corda. Para saber mais, consulte O Registo.

Enlaces

As ligações são definidas utilizando atributos em métodos, parâmetros e tipos de devolução. Um método de função é um método com um Function atributo e um atributo de gatilho aplicado a um parâmetro de entrada, como mostra o exemplo seguinte:

[Function("QueueFunction")]
[QueueOutput("myqueue-output")]
public static string Run([QueueTrigger("myqueue-items")] Book myQueueItem,
    FunctionContext context)

O atributo gatilho especifica o tipo de gatilho e liga os dados de entrada a um parâmetro do método. A função exemplo anterior é desencadeada por uma mensagem de fila, e a mensagem de fila é passada para o método no myQueueItem parâmetro.

O Function atributo marca o método como ponto de entrada de função. O nome deve ser único dentro de um projeto, começar com uma letra e conter apenas letras, números _ e - até 127 caracteres de comprimento. Project modelos muitas vezes criam um método chamado Run , mas o nome do método pode ser qualquer nome de método C# válido.

Como os projetos isolados .NET funcionam num processo de trabalho separado, as ligações não podem tirar partido de classes de encadernação ricas, ICollector<T> IAsyncCollector<T> como, CloudBlockBlob e. Também não há suporte direto para tipos herdados de SDKs de serviço subjacente, tais como [DocumentClient] e BrokeredMessage. Em vez disso, as ligações dependem de cordas, matrizes e tipos serializáveis, tais como objetos de classe antiga simples (POCOs).

Para os detonadores HTTP, tem de utilizar httpRequestData e HttpResponseData para aceder aos dados de pedido e resposta. Isto porque não tem acesso aos objetos de pedido e resposta HTTP originais quando está a ficar sem processo.

Para obter um conjunto completo de amostras de referência para a utilização de gatilhos e encadernações durante o escoramento, consulte a amostra de referência das extensões de ligação.

Enlaces de entrada

Uma função pode ter ligações de entrada zero ou mais que podem passar dados para uma função. Tal como os gatilhos, as ligações de entrada são definidas aplicando um atributo de ligação a um parâmetro de entrada. Quando a função executa, o tempo de execução tenta obter dados especificados na ligação. Os dados solicitados dependem frequentemente da informação fornecida pelo gatilho utilizando parâmetros vinculativos.

Enlaces de saída

Para escrever para uma ligação de saída, deve aplicar um atributo de ligação de saída ao método de função, que definiu como escrever ao serviço vinculado. O valor devolvido pelo método é escrito para a ligação de saída. Por exemplo, o exemplo a seguir escreve um valor de corda para uma fila de mensagens nomeada myqueue-output utilizando uma ligação de saída:

[Function("QueueFunction")]
[QueueOutput("myqueue-output")]
public static string Run([QueueTrigger("myqueue-items")] Book myQueueItem,
    FunctionContext context)
{
    var logger = context.GetLogger("QueueFunction");
    logger.LogInformation($"Book name = {myQueueItem.Name}");

    // Queue Output
    return "queue message";
}

Encadernações múltiplas de saída

Os dados escritos para uma ligação de saída é sempre o valor de devolução da função. Se precisar de escrever para mais de uma ligação de saída, tem de criar um tipo de devolução personalizado. Este tipo de devolução deve ter o atributo de ligação de saída aplicado a uma ou mais propriedades da classe. O exemplo a seguir de um gatilho HTTP escreve tanto para a resposta HTTP como para uma ligação de saída de fila:

public static class MultiOutput
{
    [Function("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, pelo que não é necessário um atributo de valor de devolução.

Acionador HTTP

HTTP triggers traduz a mensagem de pedido HTTP incoming num objeto HttpRequestData que é passado para a função. Este objeto fornece dados do pedido, Headers incluindo, , , e opcional uma Cookies Identities URL mensagem Body . Este objeto é uma representação do objeto de pedido HTTP e não do próprio pedido.

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

O seguinte código é um gatilho HTTP

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

    var response = req.CreateResponse(HttpStatusCode.OK);
    response.Headers.Add("Date", "Mon, 18 Jul 2016 16:06:00 GMT");
    response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
    
    response.WriteString("Welcome to .NET 5!!");

    return response;
}

Registo

Em .NET isolado, pode escrever para registos utilizando uma instância ILogger obtida a partir de um objeto [FunctionContext] transmitido para a sua função. Ligue para o método [GetLogger,] passando um valor de cadeia que é o nome para a categoria em que os registos são escritos. A categoria é geralmente o nome da função específica a partir da qual os registos são escritos. Para saber mais sobre categorias, consulte o artigo de monitorização.

O exemplo a seguir mostra como obter um ILogger e escrever registos dentro de uma função:

var logger = executionContext.GetLogger("HttpFunction");
logger.LogInformation("message logged");

Utilize vários métodos de ILogger para escrever vários níveis de log, tais como LogWarning ou LogError . Para saber mais sobre os níveis de registo, consulte o artigo de monitorização.

Um ILogger também é fornecido quando se utiliza uma injeção de dependência.

Diferenças com as funções da biblioteca da classe .NET

Esta secção descreve o estado atual das diferenças funcionais e comportamentais em .NET 5.0 fora do processo em comparação com as funções da biblioteca da classe .NET em execução no processo:

Recurso/comportamento Processo em curso (.NET Core 3.1) Fora de processo (.NET 5.0)
Versões .NET LTS (.NET Core 3.1) Corrente (.NET 5.0)
Pacotes centrais Microsoft.NET.Sdk.Functions Microsoft.Azure.Functions.Worker
Microsoft.Azure.Functions.Worker.Sdk
Pacotes de extensão vinculativa Microsoft.Azure.WebJobs.Extensions.* Sob microsoft.Azure.Functions.Worker.Extensions.*
Registo ILogger passou para a função ILogger obtido a partir de [FunctionContext]
Fichas de cancelamento Suportado Não suportado
Enlaces de saída Parâmetros fora Valores de retorno
Tipos de enlace de saída IAsyncCollector, [DocumentoClient,] [BrokeredMessage,]e outros tipos específicos do cliente Tipos simples, tipos serializáveis JSON e matrizes.
Encadernações múltiplas de saída Suportado Suportado
Acionador HTTP HttpRequest / ObjectResult HttpRequestData / HttpResponseData
Funções Duráveis Suportado Não suportado
Obrigações imperativas Suportado Não suportado
function.jsno artefacto Gerado Não gerado
Configuração host.js host.jse a inicialização personalizada
Injeção de dependência Suportado Suportado
Middleware Não suportado Suportado
Tempos de início a frio Típico Mais tempo, por causa do arranque a tempo. Corra no Linux em vez de Windows para reduzir potenciais atrasos.
ReadyToRun Suportado TBD

Problemas conhecidos

Para obter informações sobre soluções alternativas para saber os problemas em execução .NET funções de processo isolado, consulte esta página de assuntos conhecidos. Para relatar problemas, crie um problema neste repositório GitHub.

Passos seguintes