Guida di riferimento a Funzioni di Azure per sviluppatori di script C#

L'esperienza con gli script C# per Funzioni di Azure si basa su Azure WebJobs SDK. I dati vengono trasmessi alla funzione C# tramite argomenti del metodo. I nomi di argomento sono specificati in function.jsone sono disponibili nomi predefiniti per l'accesso a elementi quali il logger delle funzioni e i token di annullamento.

Questo articolo presuppone che l'utente abbia già letto Guida di riferimento per gli sviluppatori di Funzioni di Azure.

Per informazioni sull'uso delle librerie di classi di C#, vedere Using .NET class libraries with Azure Functions (Usare le librerie di classi di .NET con Funzioni di Azure).

Funzionamento di CSX

Il formato .csx consente di scrivere meno "boilerplate" e di concentrarsi solo sulla scrittura di una funzione C#. Includere tutti i riferimenti ad assembly e gli spazi dei nomi all'inizio del file come di consueto. Anziché eseguire il wrapping di tutti gli elementi in un spazio dei nomi e classe, definire semplicemente un metodo Run. Se è necessario includere classi, ad esempio per definire oggetti POCO (Plain Old CLR Object), si può includere una classe nello stesso file.

Associazione agli argomenti

I vari binding sono associati a una funzione C# tramite la proprietà name nella configurazione di function.json. Ogni associazione ha i propri tipi supportati; ad esempio un trigger di BLOB può supportare una stringa, un POCO o un CloudBlockBlob. I tipi supportati sono documentati negli argomenti di riferimento per ogni associazione. In un oggetto POCO devono essere definiti un metodo Get e un metodo Set per ogni proprietà.

public static void Run(string myBlob, out MyClass myQueueItem)
{
    log.Verbose($"C# Blob trigger function processed: {myBlob}");
    myQueueItem = new MyClass() { Id = "myid" };
}

public class MyClass
{
    public string Id { get; set; }
}

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 l'articolo Improper Instantiation antipattern (Antipattern non valido per la creazione di istanze).

Uso del valore restituito del metodo per l'associazione di output

È possibile usare un valore restituito del metodo per un'associazione di output, usando il nome $return in function.json:

{
    "type": "queue",
    "direction": "out",
    "name": "$return",
    "queueName": "outqueue",
    "connection": "MyStorageConnectionString",
}
public static string Run(string input, TraceWriter log)
{
    return input;
}

Scrittura di più valori di output

Per scrivere più valori in un'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 usando ICollector:

public static void Run(ICollector<string> myQueueItem, TraceWriter log)
{
    myQueueItem.Add("Hello");
    myQueueItem.Add("World!");
}

Registrazione

Per registrare l'output nei log in streaming in C#, includere un argomento di tipo TraceWriter. È consigliabile denominarlo log. Evitare di usare Console.Write in Funzioni di Azure.

TraceWriter è definito in Azure WebJobs SDK. Il livello di registrazione per TraceWriter può essere configurato in host.json.

public static void Run(string myBlob, TraceWriter log)
{
    log.Info($"C# Blob trigger function processed: {myBlob}");
}

Async

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

public async static Task ProcessQueueMessageAsync(
        string blobName,
        Stream blobInput,
        Stream blobOutput)
{
    await blobInput.CopyToAsync(blobOutput, 4096, token);
}

Token di annullamento

Alcune operazioni richiedono l'arresto normale. Mentre è sempre preferibile scrivere il codice per la gestione degli arresti anomali, per gestire le richieste di arresto normale si definisce un argomento tipizzato CancellationToken. È fornito un CancellationToken per segnalare che viene avviato un arresto dell'host.

public async static Task ProcessQueueMessageAsyncCancellationToken(
        string blobName,
        Stream blobInput,
        Stream blobOutput,
        CancellationToken token)
    {
        await blobInput.CopyToAsync(blobOutput, 4096, token);
    }

Importazione di spazi dei nomi

Se è necessario importare spazi dei nomi, è possibile farlo come al solito con la clausola using .

using System.Net;
using System.Threading.Tasks;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)

Gli spazi dei nomi seguenti vengono importati automaticamente e sono quindi facoltativi:

  • System
  • System.Collections.Generic
  • System.IO
  • System.Linq
  • System.Net.Http
  • System.Threading.Tasks
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host

Riferimento ad assembly esterni

Per gli assembly di framework aggiungere riferimenti usando la direttiva #r "AssemblyName" .

#r "System.Web.Http"

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)

Gli assembly seguenti vengono aggiunti automaticamente dall'ambiente di hosting di Funzioni di Azure:

  • mscorlib
  • System
  • System.Core
  • System.Xml
  • System.Net.Http
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host
  • Microsoft.Azure.WebJobs.Extensions
  • System.Web.Http
  • System.Net.Http.Formatting

È possibile fare riferimento agli assembly seguenti con il nome semplice (ad esempio, #r "AssemblyName"):

  • Newtonsoft.Json
  • Microsoft.WindowsAzure.Storage
  • Microsoft.ServiceBus
  • Microsoft.AspNet.WebHooks.Receivers
  • Microsoft.AspNet.WebHooks.Common
  • Microsoft.Azure.NotificationHubs

Fare riferimento ad assembly personalizzati

Per fare riferimento a un assembly personalizzato, è possibile usare un assembly condiviso o un assembly privato:

  • Gli assembly condivisi sono condivisi da tutte le funzioni all'interno di un'app di funzione. Per fare riferimento a un assembly personalizzato, caricare l'assembly nell'app di funzione, ad esempio una cartella bin nella radice dell'app per le funzioni.
  • Gli assembly privati fanno parte del contesto di una funzione specificata e supportano il caricamento laterale di versioni diverse. Gli assembly privati devono essere caricati in una cartella bin nella directory di funzione. Fare riferimento mediante il nome del file, ad esempio #r "MyAssembly.dll".

Per informazioni su come caricare i file nella cartella della funzione vedere la sezione seguente sulla gestione dei pacchetti.

Directory controllate

La directory che contiene il file di script della funzione viene controllata automaticamente per le modifiche agli assembly. Per controllare le modifiche agli assembly in altre directory, aggiungerle all'elenco watchDirectories in host.json.

Uso dei pacchetti NuGet

Per usare i pacchetti NuGet in una funzione C#, caricare un file project.json nella cartella della funzione nel file system dell'app per le funzioni. Di seguito è riportato un esempio di file project.json che aggiunge un riferimento a Microsoft.ProjectOxford.Face versione 1.1.0:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.ProjectOxford.Face": "1.1.0"
      }
    }
   }
}

Poiché è supportato solo .NET Framework 4.6, verificare che nel file project.json sia specificato net46 come illustrato qui.

Quando si carica un file project.json , il runtime ottiene i pacchetti e aggiunge automaticamente riferimenti agli assembly dei pacchetti. Non è necessario aggiungere direttive #r "AssemblyName" . Per usare i tipi definiti nei pacchetti NuGet è sufficiente aggiungere le istruzioni using necessarie al file run.csx.

Nel runtime di Funzioni NuGet ripristina le operazioni confrontando project.json e project.lock.json. Se gli indicatori di data e ora dei file non corrispondono, NuGet esegue un ripristino e aggiorna i pacchetti. In caso contrario, NuGet non esegue alcun ripristino. Pertanto, project.lock.json non deve essere distribuito, in quanto induce NuGet a saltare il ripristino del pacchetto. Per evitare la distribuzione del file di blocco, aggiungere project.lock.json al .gitignore file.

Per usare un feed NuGet personalizzato, specificare il feed in un Nuget.Config nella radice dell'app per le funzioni. Per altre informazioni, vedere Configuring NuGet behavior (Configurazione del comportamento di NuGet).

Uso di un file project.json

  1. Aprire la funzione nel portale di Azure. La scheda dei log mostra l'output di installazione del pacchetto.
  2. Per caricare un file project.json, usare uno dei metodi descritti nella sezione Come aggiornare i file delle app per le funzioni dell'argomento Guida di riferimento per gli sviluppatori di Funzioni di Azure.
  3. Dopo il caricamento del file project.json , l'output visualizzato nel log in streaming della funzione è simile all'esempio seguente:
2016-04-04T19:02:48.745 Restoring packages.
2016-04-04T19:02:48.745 Starting NuGet restore
2016-04-04T19:02:50.183 MSBuild auto-detection: using msbuild version '14.0' from 'D:\Program Files (x86)\MSBuild\14.0\bin'.
2016-04-04T19:02:50.261 Feeds used:
2016-04-04T19:02:50.261 C:\DWASFiles\Sites\facavalfunctest\LocalAppData\NuGet\Cache
2016-04-04T19:02:50.261 https://api.nuget.org/v3/index.json
2016-04-04T19:02:50.261
2016-04-04T19:02:50.511 Restoring packages for D:\home\site\wwwroot\HttpTriggerCSharp1\Project.json...
2016-04-04T19:02:52.800 Installing Newtonsoft.Json 6.0.8.
2016-04-04T19:02:52.800 Installing Microsoft.ProjectOxford.Face 1.1.0.
2016-04-04T19:02:57.095 All packages are compatible with .NETFramework,Version=v4.6.
2016-04-04T19:02:57.189
2016-04-04T19:02:57.189
2016-04-04T19:02:57.455 Packages restored.

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 void Run(TimerInfo myTimer, TraceWriter log)
{
    log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
    log.Info(GetEnvironmentVariable("AzureWebJobsStorage"));
    log.Info(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
}

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

Riutilizzo del codice CSX

È possibile usare classi e metodi definiti in altri file con estensione .csx nel file run.csx. A questo scopo, usare direttive #load nel file run.csx. Nell'esempio seguente, una routine di registrazione denominata MyLogger viene condivisa in myLogger.csx e caricata in run.csx usando la direttiva #load:

Esempio di run.csx:

#load "mylogger.csx"

public static void Run(TimerInfo myTimer, TraceWriter log)
{
    log.Verbose($"Log by run.csx: {DateTime.Now}");
    MyLogger(log, $"Log by MyLogger: {DateTime.Now}");
}

Esempio di mylogger.csx:

public static void MyLogger(TraceWriter log, string logtext)
{
    log.Verbose(logtext);
}

L'uso di un file csx condiviso è una prassi comune quando si vuole tipizzare fortemente gli argomenti tra le funzioni usando un oggetto POCO. Nell'esempio semplificato seguente, un trigger HTTP e un trigger della coda condividono un oggetto POCO denominato Order per tipizzare fortemente i dati dell'ordine:

File run.csx di esempio per il trigger HTTP:

#load "..\shared\order.csx"

using System.Net;

public static async Task<HttpResponseMessage> Run(Order req, IAsyncCollector<Order> outputQueueItem, TraceWriter log)
{
    log.Info("C# HTTP trigger function received an order.");
    log.Info(req.ToString());
    log.Info("Submitting to processing queue.");

    if (req.orderId == null)
    {
        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
    else
    {
        await outputQueueItem.AddAsync(req);
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

File run.csx di esempio per il trigger della coda:

#load "..\shared\order.csx"

using System;

public static void Run(Order myQueueItem, out Order outputQueueItem,TraceWriter log)
{
    log.Info($"C# Queue trigger function processed order...");
    log.Info(myQueueItem.ToString());

    outputQueueItem = myQueueItem;
}

File order.csx di esempio:

public class Order
{
    public string orderId {get; set; }
    public string custName {get; set;}
    public string custAddress {get; set;}
    public string custEmail {get; set;}
    public string cartId {get; set; }

    public override String ToString()
    {
        return "\n{\n\torderId : " + orderId +
                  "\n\tcustName : " + custName +             
                  "\n\tcustAddress : " + custAddress +             
                  "\n\tcustEmail : " + custEmail +             
                  "\n\tcartId : " + cartId + "\n}";             
    }
}

È possibile usare un percorso relativo con la direttiva #load :

  • #load "mylogger.csx" carica un file che si trova nella cartella della funzione.
  • #load "loadedfiles\mylogger.csx" carica un file che si trova in una sottocartella della cartella della funzione.
  • #load "..\shared\mylogger.csx" carica un file che si trova in una cartella allo stesso livello della cartella della funzione, ovvero direttamente in wwwroot.

La direttiva #load è compatibile solo con i file con estensione .csx (script C# ), non con i file con estensione .cs.

Associazione al runtime mediante associazione imperativa

In C# e altri linguaggi .NET, è possibile usare un metodo di associazione imperativa anziché dichiarativa in function.json. 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 una voce in function.json per le associazioni imperative da eseguire.
  • 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 è il 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, ad esempio, supporta sei tipi di output, ma è possibile usare solo ICollector o IAsyncCollector per T.

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.

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    using (var writer = await binder.BindAsync<TextWriter>(new BlobAttribute("samples-output/path")))
    {
        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. Ovvero, il codice ottiene l'impostazione app predefinita per la stringa di connessione dell'account di archiviazione (AzureWebJobsStorage). È possibile specificare un'impostazione app personalizzata da usare aggiungendo StorageAccountAttribute e passando la matrice di attributi in BindAsync<T>(). Ad esempio,

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    var attributes = new Attribute[]
    {    
        new BlobAttribute("samples-output/path"),
        new StorageAccountAttribute("MyStorageAccount")
    };

    using (var writer = await binder.BindAsync<TextWriter>(attributes))
    {
        writer.Write("Hello World!");
    }
}

Nella tabella seguente vengono elencati gli attributi .NET per ogni tipo di associazione e i pacchetti in cui sono definiti.

Associazione Attributo Aggiungi riferimento
Cosmos DB Microsoft.Azure.WebJobs.DocumentDBAttribute #r "Microsoft.Azure.WebJobs.Extensions.DocumentDB"
Hub eventi Microsoft.Azure.WebJobs.ServiceBus.EventHubAttribute, Microsoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.Jobs.ServiceBus"
App per dispositivi mobili Microsoft.Azure.WebJobs.MobileTableAttribute #r "Microsoft.Azure.WebJobs.Extensions.MobileApps"
Hub di notifica Microsoft.Azure.WebJobs.NotificationHubAttribute #r "Microsoft.Azure.WebJobs.Extensions.NotificationHubs"
Bus di servizio Microsoft.Azure.WebJobs.ServiceBusAttribute, Microsoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.WebJobs.ServiceBus"
Coda di archiviazione Microsoft.Azure.WebJobs.QueueAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
BLOB di archiviazione Microsoft.Azure.WebJobs.BlobAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
Tabella di archiviazione Microsoft.Azure.WebJobs.TableAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
Twilio Microsoft.Azure.WebJobs.TwilioSmsAttribute #r "Microsoft.Azure.WebJobs.Extensions.Twilio"

Passaggi successivi

Per altre informazioni, vedere le seguenti risorse: