Condividi tramite


Sviluppare funzioni della libreria di classi C# con Funzioni di Azure

Questo articolo è un'introduzione allo sviluppo di Funzioni di Azure tramite C# nelle librerie di classi .NET. Queste librerie di classi vengono usate per eseguire In-Process con il runtime di Funzioni. Le funzioni .NET possono in alternativa eseguire _isolated da Funzioni runtime, che offre diversi vantaggi. Per altre informazioni, vedere il modello di lavoro isolato. Per un confronto completo tra questi due modelli, vedere Differenze tra il modello In-Process e il modello di lavoro isolato.

Importante

Questo articolo supporta le funzioni della libreria di classi .NET eseguite in-process con il runtime. Le funzioni C# possono essere eseguite anche out-of-process e isolate dal runtime di Funzioni. Il modello di processo di lavoro isolato è l'unico modo per eseguire versioni non LTS delle app .NET e .NET Framework nelle versioni correnti del runtime di Funzioni. Per altre informazioni, vedere Funzioni del processo di lavoro isolato .NET. Per un confronto completo tra processi di lavoro isolati e funzioni .NET In-Process, vedere Differenze tra Funzioni di Azure .NET del processo di lavoro isolato e In-Process.

Gli sviluppatori C# possono anche essere interessati a uno degli articoli seguenti:

Introduzione Concetti Formazione guidata/esempi

Funzioni di Azure supporta i linguaggi di programmazione C# e script C#. Per materiale sussidiario sull'uso di C# nel portale di Azure, vedere Guida di riferimento a per sviluppatori di script C# (.csx).

Versioni supportate

Le versioni del runtime di Funzioni supportano versioni specifiche di .NET. Per altre informazioni sulle versioni di Funzioni, vedere Panoramica delle versioni del runtime per Funzioni di Azure. Il supporto della versione dipende anche dal fatto che le funzioni eseguono il processo di lavoro In-Process o isolato.

Nota

Per informazioni su come modificare la versione del runtime di Funzioni usata dall'app per le funzioni, vedere visualizzare e aggiornare la versione di runtime corrente.

La tabella seguente illustra il livello più elevato di .NET o .NET Framework che può essere usato con una versione specifica di Funzioni.

Versione del runtime di Funzioni Modello di lavoro isolato Modello In-Process5
Funzioni 4.x1 .NET 9.0 (anteprima)
.NET 8.0
.NET 6.02
.NET Framework 4.83
.NET 8.0
.NET 6.02
Funzioni 1.x4 n/d .NET Framework 4.8

1.NET 7 è stato precedentemente supportato nel modello di lavoro isolato, ma ha raggiunto la fine del supporto ufficiale il 14 maggio 2024.

2 .NET 6 raggiunge la fine del supporto ufficiale il 12 novembre 2024.

3 Il processo di compilazione richiede anche .NET SDK.

4 Il supporto termina per la versione 1.x del runtime di Funzioni di Azure il 14 settembre 2026. Per altre informazioni, vedere questo annuncio relativo al supporto. Per la prosecuzione del supporto completo, eseguire la migrazione delle app alla versione 4.x.

5 Il supporto per il modello In-Process terminerà il 10 novembre 2026. Per altre informazioni, vedere questo annuncio relativo al supporto. Per la prosecuzione del supporto completo, eseguire la migrazione delle app al modello di lavoro isolato.

Per le ultime notizie sulle versioni di Funzioni di Azure, inclusa la rimozione di versioni secondarie meno recenti specifiche, monitorare gli annunci del servizio app di Azure.

Aggiornamento a .NET 8 di destinazione

Le app che usano il modello In-Process possono essere destinate a .NET 8 seguendo i passaggi descritti in questa sezione. Tuttavia, se si sceglie di eseguire questa opzione, è comunque consigliabile iniziare a pianificare la migrazione al modello di lavoro isolato prima che il supporto per il modello In-Process termini il 10 novembre 2026.

Molte app possono modificare la configurazione dell'app per le funzioni in Azure senza aggiornamenti al codice o alla ridistribuzione. Per eseguire .NET 8 con il modello In-Process, sono necessarie tre configurazioni:

  • L'impostazione dell'applicazioneFUNCTIONS_WORKER_RUNTIME deve essere impostata con il valore "dotnet".
  • L'impostazione dell'applicazione FUNCTIONS_EXTENSION_VERSION deve essere impostata con il valore "~4".
  • L'impostazione dell'applicazione FUNCTIONS_INPROC_NET8_ENABLED deve essere impostata con il valore "~1".
  • È necessario aggiornare la configurazione dello stack per fare riferimento a .NET 8.

Il supporto per .NET 8 usa ancora la versione 4.x del runtime di Funzioni e non è necessaria alcuna modifica alla versione di runtime configurata.

Per aggiornare il progetto locale, assicurarsi prima di tutto di usare le versioni più recenti degli strumenti locali. Assicurarsi quindi che il progetto faccia riferimento alla versione 4.4.0 o successive di Microsoft.NET.Sdk.Functions. È quindi possibile modificare TargetFramework in "net8.0". È inoltre necessario aggiornare local.settings.json per includere sia FUNCTIONS_WORKER_RUNTIME impostato su "dotnet" che FUNCTIONS_INPROC_NET8_ENABLED impostato su "1".

Di seguito è riportato un esempio di un file project minimo con queste modifiche:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <AzureFunctionsVersion>v4</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.4.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

Di seguito è riportato un esempio di un file local.settings.json minimo con queste modifiche:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_INPROC_NET8_ENABLED": "1",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet"
    }
}

Se l'app usa Microsoft.Azure.DurableTask.Netherite.AzureFunctions, assicurarsi che sia destinata alla versione 1.5.3 o successiva. A causa di una modifica funzionale in .NET 8, le app con versioni precedenti del pacchetto genereranno un'eccezione di costruttore ambigua.

Potrebbe essere necessario apportare altre modifiche all'app in base al supporto della versione delle altre dipendenze.

La versione 4.x del runtime di Funzioni offre funzionalità equivalenti per .NET 6 e .NET 8. Il modello in-process non include funzionalità aggiuntive o aggiornamenti che si integrano con nuove funzionalità di .NET 8. Ad esempio, il runtime non supporta i servizi con chiave. Per sfruttare appieno le funzionalità e i miglioramenti più recenti di .NET 8, è necessario eseguire la migrazione al modello di lavoro isolato.

Progetto di libreria di classi per Funzioni

In Visual Studio il modello di progetto Funzioni di Azure crea un progetto di libreria di classi C# contenente i file seguenti:

  • host.json: archivia le impostazioni di configurazione che interessano tutte le funzioni del progetto quando vengono eseguite nell'ambiente locale o in Azure.
  • local.settings.json: archivia le impostazioni dell'app e le stringhe di connessione usate per l'esecuzione nell'ambiente locale. Questo file contiene segreti e non viene pubblicato nell'app per le funzioni in Azure. Invece, aggiungere le impostazioni dell'app all'app per le funzioni.

Quando si compila il progetto viene generata una struttura di cartelle simile all’esempio seguente nella directory di output di compilazione:

<framework.version>
 | - bin
 | - MyFirstFunction
 | | - function.json
 | - MySecondFunction
 | | - function.json
 | - host.json

Questa directory viene distribuita all'app per le funzioni in Azure. Le estensioni di associazione necessarie nella versione 2.x del runtime di Funzioni vengono aggiunte al progetto come pacchetti NuGet.

Importante

Il processo di compilazione crea un file function.json per ogni funzione. Il file function.json non viene modificato direttamente. Non è possibile modificare la configurazione di associazione o disabilitare la funzione modificando il file. Per informazioni su come disabilitare una funzione, vedere Come disabilitare le funzioni.

Metodi riconosciuti come funzioni

In una libreria di classi, una funzione è un metodo con un attributo FunctionName e trigger, come illustrato nell'esempio seguente:

public static class SimpleExample
{
    [FunctionName("QueueTrigger")]
    public static void Run(
        [QueueTrigger("myqueue-items")] string myQueueItem, 
        ILogger log)
    {
        log.LogInformation($"C# function processed: {myQueueItem}");
    }
} 

L'attributo FunctionName indica il metodo come punto di ingresso della funzione. Il nome deve essere univoco all'interno di un progetto, iniziare con una lettera e contenere solo lettere, numeri e caratteri _ e -, fino a 127 caratteri. I modelli di progetto spesso creano un metodo denominato Run, ma il nome del metodo può essere qualsiasi nome di metodo c# valido. L'esempio precedente mostra un metodo statico, ma le funzioni non devono essere statiche.

L'attributo trigger specifica il tipo di trigger e associa i dati di input a un parametro del metodo. La funzione di esempio viene attivata da un messaggio della coda e il messaggio della coda viene passato al metodo nel parametro myQueueItem.

Parametri di Method Signature

Method Signature può contenere parametri diversi da quelli usati con l'attributo trigger. Ecco alcuni degli altri parametri che è possibile includere:

L'ordine dei parametri nella firma della funzione non è rilevante. È ad esempio possibile inserire i parametri di trigger prima o dopo le altre associazioni e il parametro del logger prima o dopo i parametri di associazione o di trigger.

Associazioni di output

Una funzione può avere zero o più associazioni di output definite usando i parametri di output.

L'esempio seguente differisce dal precedente per l'aggiunta di un'associazione di coda di output denominata myQueueItemCopy. La funzione scrive il contenuto del messaggio che attiva la funzione in un nuovo messaggio di un'altra coda.

public static class SimpleExampleWithOutput
{
    [FunctionName("CopyQueueMessage")]
    public static void Run(
        [QueueTrigger("myqueue-items-source")] string myQueueItem, 
        [Queue("myqueue-items-destination")] out string myQueueItemCopy,
        ILogger log)
    {
        log.LogInformation($"CopyQueueMessage function processed: {myQueueItem}");
        myQueueItemCopy = myQueueItem;
    }
}

I valori assegnati alle associazioni di output vengono scritti quando la funzione viene chiusa. È possibile usare più associazioni di output in una funzione semplicemente assegnando valori a più parametri di output.

Gli articoli di riferimento di associazione (Code di archiviazione, ad esempio) illustrano quali tipi di parametri è possibile usare con attributi di associazione di trigger, input o output.

Esempio di espressioni di associazione

Il codice seguente ottiene il nome della coda per il monitoraggio da un'impostazione di app e l'ora di creazione del messaggio della coda nel parametro insertionTime.

public static class BindingExpressionsExample
{
    [FunctionName("LogQueueMessage")]
    public static void Run(
        [QueueTrigger("%queueappsetting%")] string myQueueItem,
        DateTimeOffset insertionTime,
        ILogger log)
    {
        log.LogInformation($"Message content: {myQueueItem}");
        log.LogInformation($"Created at: {insertionTime}");
    }
}

Function.json generato automaticamente

Il processo di compilazione crea un file function.json in una cartella della funzione nella cartella di compilazione. Come affermato in precedenza, questo file non viene modificato direttamente. Non è possibile modificare la configurazione di associazione o disabilitare la funzione modificando il file.

Lo scopo di questo file è fornire informazioni al controller di scalabilità da usare per decisioni di ridimensionamento sul piano a consumo. Per questo motivo il file contiene solo informazioni di trigger, non associazioni di input/output.

Il file function.json generato include una proprietà configurationSource che indica al runtime di usare gli attributi .NET per le associazioni invece della configurazione function.json. Ecco un esempio:

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.0.0",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "queueTrigger",
      "queueName": "%input-queue-name%",
      "name": "myQueueItem"
    }
  ],
  "disabled": false,
  "scriptFile": "..\\bin\\FunctionApp1.dll",
  "entryPoint": "FunctionApp1.QueueTrigger.Run"
}

Microsoft.NET.Sdk.Functions

La generazione del file function.json viene eseguita dal pacchetto NuGet Microsoft.NET.Sdk.Functions.

L'esempio seguente mostra le parti rilevanti dei file .csproj con framework di destinazione diversi dello stesso pacchetto di Sdk:

<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
  <AzureFunctionsVersion>v4</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
  <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.4.0" />
</ItemGroup>

Tra le dipendenze del pacchetto Sdk sono inclusi i trigger e le associazioni. Un progetto 1.x fa riferimento a trigger e associazioni 1.x perché questi trigger e associazioni hanno come destinazione .NET Framework, mentre i trigger e le associazioni 4.x hanno come destinazione .NET Core.

Il pacchetto Sdk dipende inoltre da Newtonsoft.Json e indirettamente da WindowsAzure.Storage. Queste dipendenze consentono di assicurarsi che il progetto usi le versioni dei pacchetti compatibili con la versione del runtime di Funzioni definita come destinazione del progetto. Se ad esempio è disponibile Newtonsoft.Json versione 11 per .NET Framework 4.6.1, ma il runtime di Funzioni che ha come destinazione .NET Framework 4.6.1 è compatibile solo con Newtonsoft.Json 9.0.1, anche il codice delle funzioni nel progetto deve usare Newtonsoft.Json 9.0.1.

Il codice sorgente per Microsoft.NET.Sdk.Functions è disponibile nel repository GitHub azure-functions-vs-build-sdk.

Versione del runtime locale

Per eseguire i progetti di Funzioni nel computer locale, Visual Studio usa Azure Functions Core Tools. Gli strumenti di base offrono un'interfaccia della riga di comando per il runtime di Funzioni.

Se si installano gli strumenti di base usando il pacchetto Windows Installer (MSI) o usando npm, questo non ha alcun effetto sulla versione degli strumenti di base usata da Visual Studio. Per la versione 1.x del runtime di Funzioni, Visual Studio archivia le versioni degli strumenti di base in %USERPROFILE%\AppData\Local\Azure.Functions.Cli e usa la versione più recente archiviata. Per Funzioni 4.x, gli strumenti di base sono inclusi nell'estensione Funzioni di Azure e strumenti per processi Web. Per Funzioni 1.x è possibile vedere la versione in uso nell'output della console quando si esegue un progetto di Funzioni:

[3/1/2018 9:59:53 AM] Starting Host (HostId=contoso2-1518597420, Version=2.0.11353.0, ProcessId=22020, Debug=False, Attempt=0, FunctionsExtensionVersion=)

ReadyToRun

È possibile compilare l'app per le funzioni come File binari ReadyToRun. ReadyToRun è una forma di compilazione anticipata che consente di migliorare le prestazioni di avvio per ridurre l'impatto dell'avvio a freddo durante l'esecuzione in un piano a consumo.

ReadyToRun è disponibile in .NET 6 e versioni successive e richiede la versione 4.0 del runtime di Funzioni di Azure.

Per compilare il progetto come ReadyToRun, aggiornare il file di progetto aggiungendo gli elementi <PublishReadyToRun> e <RuntimeIdentifier>. Di seguito viene riportata la configurazione per la pubblicazione in un'app per le funzioni a 32 bit di Windows.

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

Importante

A partire da .NET 6, è stato aggiunto il supporto per la compilazione di ReadyToRun compositi. Vedere Multipiattaforma ReadyToRun e restrizioni dell'architettura.

È anche possibile compilare l'app con ReadyToRun dalla riga di comando. Per ulteriori informazioni, vedere l'opzione -p:PublishReadyToRun=true in dotnet publish.

Tipi supportati per le associazioni

Ogni associazione supporta determinati tipi. Ad esempio è possibile applicare un attributo trigger di BLOB a un parametro stringa, un parametro POCO, un parametro CloudBlockBlob o uno dei molti altri tipi supportati. L'articolo di riferimento sull'associazione relativo alle associazioni BLOB elenca tutti i tipi di parametri supportati. Per altre informazioni, vedere Trigger e associazioni e i documenti di riferimento per ogni tipo di associazione.

Suggerimento

Se si prevede di usare binding HTTP o WebHook, evitare l'esaurimento delle porte che può essere causato da un'errata creazione di istanze di HttpClient. Per altre informazioni, vedere How to manage connections in Azure Functions (Come gestire le connessioni in Funzioni di Azure).

Associazione al valore restituito dal metodo

È possibile usare un valore restituito dal metodo per un'associazione di output applicando l'attributo a tale valore. Per alcuni esempi, vedere Trigger e associazioni.

Usare il valore restituito solo se la corretta esecuzione di una funzione restituisce sempre un valore da passare all'associazione di output. In caso contrario, usare ICollector o IAsyncCollector, come illustrato nella sezione seguente.

Scrittura di più valori di output

Per scrivere più valori in un'associazione di output o se una chiamata corretta alla funzione potrebbe non restituire alcun valore da passare all'associazione di output, usare i tipi ICollector o IAsyncCollector. Questi tipi sono raccolte di sola scrittura che vengono scritte nell'associazione di output durante il completamento del metodo.

Questo esempio scrive più messaggi in coda nella stessa coda usando ICollector:

public static class ICollectorExample
{
    [FunctionName("CopyQueueMessageICollector")]
    public static void Run(
        [QueueTrigger("myqueue-items-source-3")] string myQueueItem,
        [Queue("myqueue-items-destination")] ICollector<string> myDestinationQueue,
        ILogger log)
    {
        log.LogInformation($"C# function processed: {myQueueItem}");
        myDestinationQueue.Add($"Copy 1: {myQueueItem}");
        myDestinationQueue.Add($"Copy 2: {myQueueItem}");
    }
}

Async

Per rendere una funzione asincrona, usare la parola chiave async e restituire un oggetto Task.

public static class AsyncExample
{
    [FunctionName("BlobCopy")]
    public static async Task RunAsync(
        [BlobTrigger("sample-images/{blobName}")] Stream blobInput,
        [Blob("sample-images-copies/{blobName}", FileAccess.Write)] Stream blobOutput,
        CancellationToken token,
        ILogger log)
    {
        log.LogInformation($"BlobCopy function processed.");
        await blobInput.CopyToAsync(blobOutput, 4096, token);
    }
}

Non è possibile usare i parametri out in funzioni asincrone. Per le associazioni di output, usare invece il valore restituito di funzione o un oggetto agente di raccolta.

Token di annullamento

Una funzione può accettare un parametro CancellationToken, che consente al sistema operativo di notificare il codice quando la funzione sta per essere terminata. È possibile usare questa notifica per assicurarsi che la funzione non termini in modo imprevisto lasciando i dati in uno stato incoerente.

Si consideri il caso in cui si dispone di una funzione che elabora i messaggi in batch. La seguente funzione attivata dal bus di servizi di Azure elabora una matrice di oggetti ServiceBusReceivedMessage, che rappresenta un batch di messaggi in ingresso da elaborare da una chiamata di funzione specifica:

using Azure.Messaging.ServiceBus;
using System.Threading;

namespace ServiceBusCancellationToken
{
    public static class servicebus
    {
        [FunctionName("servicebus")]
        public static void Run([ServiceBusTrigger("csharpguitar", Connection = "SB_CONN")]
               ServiceBusReceivedMessage[] messages, CancellationToken cancellationToken, ILogger log)
        {
            try
            { 
                foreach (var message in messages)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        log.LogInformation("A cancellation token was received. Taking precautionary actions.");
                        //Take precautions like noting how far along you are with processing the batch
                        log.LogInformation("Precautionary activities --complete--.");
                        break;
                    }
                    else
                    {
                        //business logic as usual
                        log.LogInformation($"Message: {message} was processed.");
                    }
                }
            }
            catch (Exception ex)
            {
                log.LogInformation($"Something unexpected happened: {ex.Message}");
            }
        }
    }
}

Registrazione

Nel codice della funzione è possibile scrivere l'output nei log visualizzati che vengono visualizzati come tracce in Application Insights. Il modo consigliato per scrivere nei log consiste nell'includere un parametro di tipo ILogger, denominato in genere log. La versione 1.x del runtime Funzioni usava TraceWriter, che scriveva anche in Application Insights, ma non supporta la registrazione strutturata. Non usare Console.Write per scrivere i log, poiché questi dati non vengono acquisiti da Application Insights.

ILogger

Nella definizione della funzione includere un parametro ILogger che supporta la registrazione strutturata.

Con un oggetto ILogger è possibile chiamare i metodi di estensione su ILoggerLog<level> per creare i log. Il codice seguente scrive log Information categoria Function.<YOUR_FUNCTION_NAME>.User.:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger logger)
{
    logger.LogInformation("Request for item with key={itemKey}.", id);

Per altre informazioni su come Funzioni implementa ILogger, vedere Raccolta di dati di telemetria. Le categorie precedute da Function presuppongono l'uso di un'istanza di ILogger. Se si sceglie di usare invece un ILogger<T>, il nome della categoria può essere invece basato su T.

Registrazione strutturata

I parametri usati nel messaggio del log sono determinati dall'ordine dei segnaposto, non dai nomi. Si supponga di avere il codice seguente:

string partitionKey = "partitionKey";
string rowKey = "rowKey";
logger.LogInformation("partitionKey={partitionKey}, rowKey={rowKey}", partitionKey, rowKey);

Se si mantenere la stessa stringa del messaggio e si inverte l'ordine dei parametri, il testo del messaggio risultante mostrerebbe i valori in maniera errata.

I segnaposto vengono gestiti in modo da poter eseguire la registrazione strutturata. Application Insights archivia le coppie nome-valore del parametro e la stringa del messaggio. Il risultato è che gli argomenti del messaggio diventano campi su cui è possibile eseguire delle query.

Se la chiamata al metodo del logger è simile all'esempio precedente, è possibile eseguire una query per il campo customDimensions.prop__rowKey. Il prefisso prop__ viene aggiunto per verificare che non ci siano conflitti tra i campi aggiunti dal runtime e i campi aggiunti dal codice funzione.

È anche possibile eseguire query sulla stringa del messaggio originale facendo riferimento al campo customDimensions.prop__{OriginalFormat}.

Ecco una rappresentazione JSON di esempio dei dati customDimensions:

{
  "customDimensions": {
    "prop__{OriginalFormat}":"C# Queue trigger function processed: {message}",
    "Category":"Function",
    "LogLevel":"Information",
    "prop__message":"c9519cbf-b1e6-4b9b-bf24-cb7d10b1bb89"
  }
}

Log della telemetria personalizzata

È disponibile una versione di Application Insights SDK specifica per Funzioni di Azure, che consente di inviare dati di telemetria personalizzati dalle funzioni in uso ad Application Insights: Microsoft.Azure.WebJobs.Logging.ApplicationInsights. Al prompt dei comandi, usare il comando seguente per installare il pacchetto:

dotnet add package Microsoft.Azure.WebJobs.Logging.ApplicationInsights --version <VERSION>

In questo comando sostituire <VERSION> con una versione del pacchetto che supporta la versione installata di Microsoft.Azure.WebJobs.

L'esempio C# seguente usa l'API di telemetria personalizzata. L'esempio fa riferimento a una libreria di classi .NET, ma il codice di Application Insights è lo stesso per lo script C#.

La versione 2.x e le versioni successive del runtime usano le funzionalità più recenti in Application Insights per correlare automaticamente i dati di telemetria con l'operazione corrente. Non è necessario impostare manualmente i campi Id, ParentId o Name dell'operazione.

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
using System.Linq;

namespace functionapp0915
{
    public class HttpTrigger2
    {
        private readonly TelemetryClient telemetryClient;

        /// Using dependency injection will guarantee that you use the same configuration for telemetry collected automatically and manually.
        public HttpTrigger2(TelemetryConfiguration telemetryConfiguration)
        {
            this.telemetryClient = new TelemetryClient(telemetryConfiguration);
        }

        [FunctionName("HttpTrigger2")]
        public Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req, ExecutionContext context, ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            DateTime start = DateTime.UtcNow;

            // Parse query parameter
            string name = req.Query
                .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
                .Value;

            // Write an event to the customEvents table.
            var evt = new EventTelemetry("Function called");
            evt.Context.User.Id = name;
            this.telemetryClient.TrackEvent(evt);

            // Generate a custom metric, in this case let's use ContentLength.
            this.telemetryClient.GetMetric("contentLength").TrackValue(req.ContentLength);

            // Log a custom dependency in the dependencies table.
            var dependency = new DependencyTelemetry
            {
                Name = "GET api/planets/1/",
                Target = "swapi.co",
                Data = "https://swapi.co/api/planets/1/",
                Timestamp = start,
                Duration = DateTime.UtcNow - start,
                Success = true
            };
            dependency.Context.User.Id = name;
            this.telemetryClient.TrackDependency(dependency);

            return Task.FromResult<IActionResult>(new OkResult());
        }
    }
}

In questo esempio, i dati delle metriche personalizzate vengono aggregati dall'host prima di essere inviati alla tabella customMetrics. Per altre informazioni, vedere la documentazione di GetMetric in Application Insights.

Quando si esegue in locale, è necessario aggiungere l'impostazione APPINSIGHTS_INSTRUMENTATIONKEY, con la chiave di Application Insights, al file local.settings.json.

Non chiamare TrackRequest o StartOperation<RequestTelemetry> poiché comparirebbero richieste duplicate per una chiamata di funzione. Il runtime di Funzioni rileva automaticamente le richieste.

Non impostare telemetryClient.Context.Operation.Id. Si tratta di un'impostazione globale che causa errori di correlazione quando molte funzioni vengono eseguite contemporaneamente. Creare invece una nuova istanza di telemetria (DependencyTelemetry, EventTelemetry) e modificare la relativa proprietà Context. Passare quindi l'istanza di telemetria al metodo Track corrispondente in TelemetryClient (TrackDependency(), TrackEvent(), TrackMetric()). Questo metodo assicura che la telemetria disponga dei dettagli di correlazione corretti per la chiamata di funzione corrente.

Test delle funzioni

Gli articoli seguenti illustrano come eseguire una funzione della libreria di classi C# In-Process in locale a scopo di test:

Variabili di ambiente

Per ottenere una variabile di ambiente o un valore di impostazione dell'app, usare System.Environment.GetEnvironmentVariablecome illustrato nell'esempio di codice seguente:

public static class EnvironmentVariablesExample
{
    [FunctionName("GetEnvironmentVariables")]
    public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
    {
        log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
        log.LogInformation(GetEnvironmentVariable("AzureWebJobsStorage"));
        log.LogInformation(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
    }

    private static string GetEnvironmentVariable(string name)
    {
        return name + ": " +
            System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
    }
}

Le impostazioni dell'app possono essere lette dalle variabili di ambiente durante lo sviluppo in locale e durante l'esecuzione in Azure. Durante lo sviluppo in locale, le impostazioni dell'app vengono ricavate dalla raccolta Values nel file local.settings.json. In entrambi gli ambienti, locale e Azure, GetEnvironmentVariable("<app setting name>") recupera il valore dell'impostazione dell'app denominata. In caso di esecuzione locale, ad esempio, se il file local.settings.json contiene { "Values": { "WEBSITE_SITE_NAME": "My Site Name" } }, viene restituito "My Site Name".

La proprietà System.Configuration.ConfigurationManager.AppSettings è un'API alternativa per il recupero di valori di impostazione dell'app, ma è consigliabile usare GetEnvironmentVariable come illustrato di seguito.

Associazione in fase di esecuzione

In C# e altri linguaggi .NET, è possibile usare un modello di associazione imperativo anziché dichiarativo negli attributi. L'associazione imperativa è utile quando i parametri di associazione devono essere calcolati in fase di runtime invece che in fase di progettazione. Con questo modello è possibile associare rapidamente i dati ad associazioni di input e output supportate nel codice della funzione.

Definire un'associazione imperativa, come segue:

  • Non includere un attributo nella firma della funzione per le associazioni imperative.

  • Passare un parametro di input Binder binder o IBinder binder.

  • Usare il seguente modello C# per eseguire l'associazione dati.

    using (var output = await binder.BindAsync<T>(new BindingTypeAttribute(...)))
    {
        ...
    }
    

    BindingTypeAttribute è l'attributo .NET che definisce l'associazione e T è un tipo di input o output supportato da quel tipo di associazione. T non può essere un tipo di parametro out (ad esempio out JObject). L'associazione di output della tabella App per dispositivi mobili supporta ad esempio sei tipi di output, ma è possibile usare solo ICollector<T> o IAsyncCollector<T> con l'associazione imperativa.

Esempio con un solo attributo

L'esempio di codice seguente crea un associazione di output del BLOB di archiviazione con percorso del BLOB definito in fase di esecuzione, quindi scrive una stringa per il BLOB.

public static class IBinderExample
{
    [FunctionName("CreateBlobUsingBinder")]
    public static void Run(
        [QueueTrigger("myqueue-items-source-4")] string myQueueItem,
        IBinder binder,
        ILogger log)
    {
        log.LogInformation($"CreateBlobUsingBinder function processed: {myQueueItem}");
        using (var writer = binder.Bind<TextWriter>(new BlobAttribute(
                    $"samples-output/{myQueueItem}", FileAccess.Write)))
        {
            writer.Write("Hello World!");
        };
    }
}

BlobAttribute definisce l'associazione di input o output del BLOB di archiviazione e TextWriter è un tipo di associazione di output supportato.

Esempio con più attributi

L'esempio precedente ottiene l'impostazione dell'app per la stringa di connessione dell'account di archiviazione principale dell'app, ovvero AzureWebJobsStorage. È possibile specificare un'impostazione app personalizzata da usare per l'account di archiviazione aggiungendo StorageAccountAttribute e passando la matrice di attributi in BindAsync<T>(). Usare un parametro Binder e non IBinder. Ad esempio:

public static class IBinderExampleMultipleAttributes
{
    [FunctionName("CreateBlobInDifferentStorageAccount")]
    public async static Task RunAsync(
            [QueueTrigger("myqueue-items-source-binder2")] string myQueueItem,
            Binder binder,
            ILogger log)
    {
        log.LogInformation($"CreateBlobInDifferentStorageAccount function processed: {myQueueItem}");
        var attributes = new Attribute[]
        {
        new BlobAttribute($"samples-output/{myQueueItem}", FileAccess.Write),
        new StorageAccountAttribute("MyStorageAccount")
        };
        using (var writer = await binder.BindAsync<TextWriter>(attributes))
        {
            await writer.WriteAsync("Hello World!!");
        }
    }
}

Trigger e associazioni

Questa tabella mostra le associazioni supportate nelle versioni principali del runtime di Funzioni di Azure:

Type 1.x1 2.x e versioni successive2 Trigger Input Output
Archiviazione BLOB
Azure Cosmos DB
Esplora dati di Azure
Azure SQL
Dapr4
Griglia di eventi
Hub eventi
HTTP e webhook
Hub IoT
Kafka3
App per dispositivi mobili
Hub di notifica di Azure
Archiviazione code
Redis
RabbitMQ3
SendGrid
Bus di servizio
SignalR
Archiviazione tabelle
Timer
Twilio

Note:

  1. Il supporto terminerà per la versione 1.x del runtime di Funzioni di Azure il 14 settembre 2026. È consigliabile eseguire la migrazione delle app alla versione 4.x per il supporto completo.
  2. A partire dal runtime della versione 2.x, devono essere registrate tutte le associazioni tranne HTTP e Timer. Vedere Registrare le estensioni delle associazioni.
  3. I trigger non sono supportati nel piano A consumo. Richiede trigger basati sul runtime.
  4. Supportati solo in Kubernetes, IoT Edge e altre modalità self-hosted.

Passaggi successivi