Compartir a través de


Desarrollo de funciones de la biblioteca de clases de C# con Azure Functions

Este artículo es una introducción al desarrollo de Azure Functions mediante el uso de C# en las bibliotecas de clases .NET. Estas bibliotecas de clases se usan para la ejecución en proceso con el runtime de Functions. Las funciones de .NET también pueden ejecutar _isolated desde el runtime de Functions, lo que ofrece varias ventajas. Para más información, consulte el modelo de trabajo aislado. Para obtener una comparación completa entre estos dos modelos, consulte Diferencias entre el modelo en proceso y el modelo de trabajo aislado.

Importante

En este artículo se admiten las funciones de la biblioteca de clases .NET que se ejecutan en proceso con el entorno en tiempo de ejecución. Las funciones de C# también se pueden ejecutar fuera de proceso y aisladas del entorno de ejecución de Functions. El modelo de proceso de trabajo aislado es la única manera de ejecutar versiones que no son LTS de aplicaciones de .NET Framework y .NET en versiones actuales del runtime de Functions. Para más información, vea Funciones de proceso de trabajo aislados de .NET. Para obtener una comparación completa entre las funciones de .NET en proceso y de proceso de trabajo aislado, consulte Diferencias entre Azure Functions con .NET en proceso y de proceso de trabajo aislado.

Como desarrollador de C#, puede que también le interese uno de los artículos siguientes:

Introducción Conceptos Ejemplos o aprendizaje guiado

Azure Functions es compatible con C# y con los lenguajes de programación de scripts de C#. Si busca orientación sobre cómo usar C# en Azure Portal, vea la referencia para desarrolladores de scripts de C#.

Versiones compatibles

Las versiones del tiempo de ejecución de Functions son compatibles con versiones específicas de .NET. Para obtener más información sobre las versiones de Functions, consulte Introducción a las versiones de tiempo de ejecución de Azure Functions. La compatibilidad con versiones depende de si las funciones se ejecutan en proceso o como proceso de trabajo aislado.

Nota

Para aprender a cambiar la versión del runtime de Functions que usa la aplicación de funciones, consulte la sesión Visualización y actualización de la versión actual del entorno de ejecución.

En la tabla siguiente, se muestra el nivel más alto de .NET o .NET Framework que se puede usar con una versión específica de Functions.

Versiones del entorno en tiempo de ejecución de Functions Modelo de trabajo aislado Modelo en proceso5
Functions 4.x1 .NET 9.0 (versión preliminar)
.NET 8.0
.NET 6.02
.NET Framework 4.83
.NET 8.0
.NET 6.02
Functions 1.x4 N/D .NET Framework 4.8

1 .NET 7 se admitía anteriormente en el modelo de trabajo aislado, pero llegó al final del soporte técnico oficial el 14 de mayo de 2024.

2 .NET 6 llega al final del soporte oficial el 12 de noviembre de 2024.

3 El proceso de compilación también requiere el SDK de .NET.

4 La compatibilidad finaliza para la versión 1.x del runtime de Azure Functions el 14 de septiembre de 2026. Para más información, consulte este anuncio de soporte. Para seguir teniendo soporte completo, debería migrar sus aplicaciones a la versión 4.x.

5El soporte técnico finalizará para el modelo en proceso el 10 de noviembre de 2026. Para más información, consulte este anuncio de soporte. Para seguir teniendo soporte técnico completo, debería migrar sus aplicaciones al modelo de trabajo aislado.

Para obtener las últimas noticias sobre las versiones de Azure Functions, incluida la eliminación de versiones secundarias específicas anteriores, revise los anuncios de Azure App Service.

Actualización a .NET 8 de destino

Las aplicaciones que usan el modelo en proceso pueden tener como destino .NET 8 siguiendo los pasos descritos en esta sección. Sin embargo, si decide ejercer esta opción, debe comenzar a planear la migración de al modelo de trabajo aislado de antemano de Soporte con el modelo en proceso el 10 de noviembre de 2026.

Muchas aplicaciones pueden cambiar la configuración de la aplicación de funciones en Azure sin necesidad de actualizar el código o volver a implementar. Para ejecutar .NET 8 con el modelo en proceso, se requieren tres configuraciones:

  • La configuración de la aplicación FUNCTIONS_WORKER_RUNTIME debe establecerse con el valor "dotnet".
  • La configuración FUNCTIONS_EXTENSION_VERSION de la aplicación debe establecerse con el valor "~4".
  • La configuración de la aplicación FUNCTIONS_INPROC_NET8_ENABLED debe establecerse con el valor "1".
  • Debe actualizar la configuración de la pila para hacer referencia a .NET 8.

El soporte con .NET 8 sigue usando la versión 4.x del entorno de ejecución de Functions y no se requiere ningún cambio en la versión del entorno de ejecución configurado.

Para actualizar el proyecto local, primero asegúrese de que usa las versiones más recientes de las herramientas locales. A continuación, asegúrese de que el proyecto hace referencia a versión 4.4.0 o posterior de Microsoft.NET.Sdk.Functions. Después, puede cambiar el TargetFramework a "net8.0". También debe actualizar local.settings.json para incluir ambos FUNCTIONS_WORKER_RUNTIME establecer en "dotnet" y FUNCTIONS_INPROC_NET8_ENABLED establecer en "1".

A continuación se muestra un ejemplo de un archivo project mínimo con estos cambios:

<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>

A continuación se muestra un ejemplo de un archivo local.settings.json mínimo con estos cambios:

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

Si la aplicación usa Microsoft.Azure.DurableTask.Netherite.AzureFunctions, asegúrese de que tiene como destino la versión 1.5.3 o posterior. Debido a un cambio de comportamiento en .NET 8, las aplicaciones con versiones anteriores del paquete producirán una excepción de constructor ambigua.

Es posible que tenga que realizar otros cambios en la aplicación en función de la compatibilidad con versiones de sus otras dependencias.

La versión 4.x del entorno de ejecución de Functions proporciona funcionalidad equivalente para .NET 6 y .NET 8. El modelo en proceso no incluye características ni actualizaciones adicionales que se integran con nuevas funcionalidades de .NET 8. Por ejemplo, el entorno de ejecución no admite servicios con claves. Para aprovechar al máximo las últimas funcionalidades y mejoras de .NET 8, debe migrar al modelo de trabajo aislado.

Proyecto de biblioteca de clases de Functions

En Visual Studio, la plantilla de proyecto de Azure Functions crea un proyecto de biblioteca de clases de C# que contiene los archivos siguientes:

Al compilar el proyecto, se genera una estructura de carpetas que se parece al siguiente ejemplo del directorio de salida de la compilación:

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

Este directorio es lo que se implementa en la aplicación de función en Azure. Las extensiones de enlace necesarias en la versión 2.x del sistema en tiempo de ejecución de Functions se agregan al proyecto como paquetes de NuGet.

Importante

El proceso de compilación crea un archivo function.json para cada función. Este archivo function.json no está pensado para que se pueda modificar directamente. No se puede cambiar la configuración del enlace ni deshabilitar la función mediante la edición de este archivo. Para obtener información sobre cómo deshabilitar una función, consulte How to disable functions (Cómo deshabilitar funciones).

Métodos reconocidos como funciones

En una biblioteca de clases, una función es un método con un atributo FunctionName y un atributo desencadenador, tal como se muestra en el ejemplo siguiente:

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

El atributo FunctionName marca el método como punto de entrada de una función. El nombre debe ser único dentro de un proyecto, debe empezar por una letra y solo puede incluir letras, números, _ y -, y hasta 127 caracteres. Las plantillas de proyecto suelen crear un método denominado Run, pero el nombre de método puede ser cualquier nombre de método de C# válido. En el ejemplo anterior se muestra el método estático que se está utilizando, pero no es necesario que las funciones sean estáticas.

El atributo desencadenador especifica el tipo de desencadenador y enlaza los datos de entrada a un parámetro del método. La función de ejemplo se desencadena mediante un mensaje de cola, y este último se pasa al método en el myQueueItem parámetro.

Parámetros de la firma del método

La firma del método puede contener parámetros distintos del usado con el atributo desencadenador. Estos son algunos de los otros parámetros que puede incluir:

  • Enlaces de entrada y salida marcados como tales con atributos.
  • Un parámetro ILogger o TraceWriter (ILogger exclusivamente) para el TraceWriter.
  • Un parámetro CancellationToken para el CancellationToken.
  • Parámetros de expresiones de enlace para obtener metadatos de desencadenador.

El orden de los parámetros en la signatura de función no es importante. Por ejemplo, puede colocar los parámetros de desencadenador antes o después de otros enlaces y puede colocar el parámetro de registrador antes o después de los parámetros de desencadenador o enlace.

Enlaces de salida

Una función puede tener cero o varios enlaces de salida definidos mediante parámetros de salida.

En el ejemplo siguiente se modifica el ejemplo anterior mediante la adición de un enlace de la cola de salida llamado myQueueItemCopy. La función escribe el contenido del mensaje que desencadena la función en un nuevo mensaje de una cola distinta.

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;
    }
}

Los valores asignados a los enlaces de salida se escriben cuando finaliza la función. Puede usar más de un enlace de salida en una función simplemente asignando valores a varios parámetros de salida.

En los artículos de referencia de enlace (colas de almacenamiento, por ejemplo) se explica qué tipos de parámetros puede usar con los atributos de enlace de entrada o salida y desencadenador.

Ejemplo de expresiones de enlace

El código siguiente recibe el nombre de la cola que se va a supervisar desde una configuración de aplicación y recibe la hora de creación del mensaje de cola en el parámetro 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}");
    }
}

Archivo function.json generado automáticamente

El proceso de compilación crea un archivo function.json en una carpeta de la función en la carpeta de compilación. Como se indicó anteriormente, este archivo no está pensado para que se pueda modificar directamente. No se puede cambiar la configuración del enlace ni deshabilitar la función mediante la edición de este archivo.

El propósito de este archivo es proporcionar información al controlador de escala que se usará para escalar decisiones en el plan de consumo. Por esta razón, el archivo solo tiene información del desencadenador, no sobre los enlaces de entrada o salida.

El archivo function.json generado incluye una propiedad configurationSource que indica el tiempo de ejecución para utilizar atributos de .NET para los enlaces, en lugar de la configuración de function.json. Este es un ejemplo:

{
  "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

El archivo function.json se genera mediante el paquete NuGet Microsoft.NET.Sdk.Functions.

En el siguiente ejemplo se muestran las partes pertinentes de los archivos .csproj que tienen plataformas de destino diferentes del mismo paquete Sdk:

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

Importante

A partir de la versión 4.0.6517 de Core Tools, los proyectos de modelos en proceso deben hacer referencia a la versión 4.5.0 o posterior de Microsoft.NET.Sdk.Functions. Si se usa una versión anterior, se producirá un error en el comando func start.

Entre las dependencias del paquete Sdk se encuentran los desencadenadores y los enlaces. Un proyecto 1.x hace referencia a los enlaces y desencadenadores 1.x porque tienen como destino .NET Framework, mientras que los enlaces y desencadenadores 4.x tienen como destino .NET Core.

El Sdk paquete también depende de Sdk e, indirectamente, de WindowsAzure.Storage. Estas dependencias garantizan que el proyecto utiliza las versiones de los paquetes que funcionan con la versión de Functions Runtime que el proyecto tiene como destino. Por ejemplo, Newtonsoft.Json tiene la versión 11 para .NET Framework 4.6.1, pero la instancia de Functions Runtime que tiene como destino .NET Framework 4.6.1 solo es compatible con Newtonsoft.Json 9.0.1. Por tanto, el código de función de ese proyecto también tiene que usar Newtonsoft.Json 9.0.1.

El código fuente de Microsoft.NET.Sdk.Functions está disponible en el repositorio de GitHub Microsoft.NET.Sdk.Functions.

Versión del entorno de ejecución local

Visual Studio usa Azure Functions Core Tools para ejecutar proyectos de Functions en el equipo local. Core Tools es una interfaz de línea de comandos para Functions Runtime.

Si instala Core Tools mediante el paquete de Windows Installer (MSI) o mediante npm, no afecta a la versión de Core Tools que usa Visual Studio. Para la versión 1.x de Functions Runtime, Visual Studio almacena Core Tools en %USERPROFILE%\AppData\Local\Azure.Functions.Cli y utiliza la versión más reciente almacenada allí. Para Functions 4.x, Core Tools se incluye en la extensión Azure Functions and Web Jobs Tools. Para Functions 1.x, puede ver qué versión se usa en la salida de la consola cuando se ejecuta un proyecto de Functions:

[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

La aplicación de funciones se puede compilar como archivos binarios de ReadyToRun. ReadyToRun es una forma de compilación Ahead Of Time que puede mejorar el rendimiento de inicio para ayudar a reducir el impacto del inicio en frío cuando se ejecuta en un plan de consumo.

ReadyToRun está disponible en .NET 6 (y versiones posteriores) y requiere la versión 4.0 de Azure Functions Runtime.

Para compilar un proyecto como ReadyToRun, actualice el archivo del proyecto agregando los elementos <PublishReadyToRun> y <RuntimeIdentifier>. A continuación se encuentra la configuración para publicar en una aplicación de funciones de Windows de 32 bits.

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

Importante

A partir de .NET 6, se ha agregado compatibilidad con la compilación Composite ReadyToRun. Vea ReadyToRun Cross platform and architecture restrictions, (Restricciones de arquitectura y multiplataforma de ReadyToRun).

La aplicación también se puede compilar con ReadyToRun desde la línea de comandos. Para más información, consulte la opción -p:PublishReadyToRun=true en dotnet publish.

Tipos compatibles para los enlaces

Cada enlace tiene sus propios tipos compatibles; por ejemplo, un atributo desencadenador de blobs puede aplicarse a un parámetro de cadena, un parámetro POCO, un parámetro CloudBlockBlob o cualquiera de los demás tipos compatibles. En el artículo de referencia sobre los enlaces de blobs se enumeran todos los tipos de parámetros compatibles. Para obtener más información, vea el artículo sobre desencadenadores y enlaces y los documentos de referencia sobre enlaces para cada tipo de enlace.

Sugerencia

Si planea usar los enlaces HTTP o WebHook, debe evitar el agotamiento de puertos que puede deberse a la creación incorrecta de instancias de HttpClient. Para más información, consulte How to manage connections in Azure Functions (Administración de conexiones en Azure Functions).

Enlace al valor devuelto del método

Puede usar un valor devuelto de método para un enlace de salida mediante la aplicación del atributo al valor devuelto de método. Para ver ejemplos, consulte Desencadenadores y enlaces.

Utilice el valor devuelto solo si una ejecución de función correcta siempre da como resultado un valor devuelto para pasar al enlace de salida. En caso contrario, use ICollector o IAsyncCollector, como se muestra en la sección siguiente.

Escribir varios valores de salida

Para escribir varios valores en un enlace de salida, o si una invocación de función correcta podría no dar nada como resultado para pasar al enlace de salida, use los tipos ICollector o IAsyncCollector. Estos tipos son colecciones de solo escritura que se escriben en el enlace de salida cuando se completa el método.

En este ejemplo se escriben varios mensajes en cola en la misma cola mediante 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

Para convertir una función en asincrónica, use la palabra clave async y devuelva un objeto 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);
    }
}

No puede usar parámetros out en funciones asincrónicas. Para los enlaces de salida, utilice el valor devuelto de función o un objeto recopilador en su lugar.

Tokens de cancelación

Una función puede aceptar un parámetro CancellationToken que permite que el sistema operativo notifique al código cuando la función esté a punto de finalizar. Puede utilizar esta notificación para asegurarse de que la función no se termina inesperadamente en una forma que deje los datos en un estado incoherente.

Piense en un caso en el que tiene una función que procesa mensajes por lotes. La siguiente función desencadenada por Azure Service Bus procesa una matriz de objetos ServiceBusReceivedMessage, que representa un lote de mensajes entrantes para procesarlos mediante una invocación de función específica:

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}");
            }
        }
    }
}

Registro

En el código de función, puede escribir la salida de los registros que aparecen como seguimientos de Application Insights. La manera recomendada de escribir en los registros es incluir un parámetro de tipo ILogger, que normalmente se denomina log. Versión 1.x del entorno de ejecución de Functions usado TraceWriter, que también escribe en Application Insights, pero no admite el registro estructurado. No use Console.Write para escribir los registros, ya que Application Insights no captura los datos.

ILogger

En la definición de la función, incluya un parámetro ILogger, que admite el registro estructurado.

Con un objeto ILogger, llamará a los métodos de extensión en ILogger Log<level> para crear registros. El código siguiente escribe registros Information con la categoría Function.<YOUR_FUNCTION_NAME>.User.:

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

Para obtener más información sobre cómo Functions implementa ILogger, consulte ILogger. Las categorías que tienen el prefijo Function suponen que está usando una instancia de ILogger. Si, por el contrario, usa ILogger<T>, el nombre de la categoría puede basarse en T.

Registro estructurado

El orden de los marcadores de posición, no sus nombres, determina qué parámetros se usan en el mensaje del registro. Supongamos que tiene el siguiente código:

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

Si mantiene la misma cadena de mensaje e invierte el orden de los parámetros, el texto del mensaje resultante tendrá los valores en los lugares incorrectos.

Los marcadores de posición se controlan de esta forma para que pueda hacer el registro estructurado. Application Insights almacena los pares de nombre-valor del parámetro y la cadena de mensaje. Como resultado, los argumentos del mensaje se convierten en campos que puede consultar.

Si la llamada de método del registrador tiene un aspecto similar al ejemplo anterior, puede consultar el campo customDimensions.prop__rowKey. Se agrega el prefijo prop__ para asegurar que no haya ninguna colisión entre los campos que agrega el tiempo de ejecución y los campos que agrega el código de la función.

También puede consultar la cadena del mensaje original haciendo referencia al campo customDimensions.prop__{OriginalFormat}.

Esta es una representación de JSON de ejemplo de los datos customDimensions:

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

Registro de la telemetría personalizada

Hay una versión específica de Functions del SDK de Application Insights que puede usar para enviar datos de telemetría personalizados desde las funciones a Application Insights: Microsoft.Azure.WebJobs.Logging.ApplicationInsights. Use el comando siguiente desde el símbolo del sistema para instalar este paquete:

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

En este comando, reemplace <VERSION> por una versión de este paquete que admita la versión instalada de <VERSION>.

En el siguiente ejemplo de C# se usa la API de telemetría personalizada. El ejemplo es para una biblioteca de clases. NET, pero el código de Application Insights es el mismo para la secuencia de comandos de C#.

La versión 2.x y versiones posteriores del entorno de ejecución utilizan las características más recientes de Application Insights para correlacionar automáticamente la telemetría con la operación actual. No es necesario establecer manualmente los campos de operación Id, ParentId o Name.

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());
        }
    }
}

En este ejemplo, el host agrega los datos de métricas personalizados antes de enviarlos a la tabla customMetrics. Para obtener más información, consulte la documentación de GetMetric en Application Insights.

Cuando se ejecuta localmente, debe agregar el valor APPINSIGHTS_INSTRUMENTATIONKEY, con la clave de Application Insights, al archivo APPINSIGHTS_INSTRUMENTATIONKEY.

No llame a TrackRequest ni a StartOperation<RequestTelemetry>, ya que verá solicitudes duplicadas de invocaciones de función. El tiempo de ejecución de Functions realiza un seguimiento automático de las solicitudes.

No establezca telemetryClient.Context.Operation.Id. Esta configuración global provoca una correlación incorrecta cuando muchas funciones se ejecuten de manera simultánea. En su lugar, cree una instancia de telemetría nueva (DependencyTelemetry, EventTelemetry) y modifique su propiedad Context. Luego, pase la instancia de telemetría al método Track correspondiente en TelemetryClient (TrackDependency(), TrackEvent(), TrackMetric()). Este método garantiza que la telemetría tenga los detalles de correlación correctos para la invocación actual de la función.

Funciones de prueba

En los artículos siguientes se muestra cómo ejecutar una función de biblioteca de clases de C# en proceso localmente con fines de prueba:

Variables de entorno

Para obtener una variable de entorno o un valor de configuración de aplicación, use System.Environment.GetEnvironmentVariable, como se muestra en el ejemplo de código siguiente:

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);
    }
}

La configuración de la aplicación se puede leer de las variables de entorno tanto cuando se desarrolla de manera local como cuando se ejecuta en Azure. Cuando se desarrolla de manera local, la configuración de la aplicación procede de la colección Values en el archivo Values. En ambos entornos, tanto local como Azure, GetEnvironmentVariable("<app setting name>") recupera el valor de la configuración de aplicación con nombre. Por ejemplo, cuando se ejecuta de manera local, se devolvería "My Site Name" si el archivo local.settings.json contiene { "Values": { "WEBSITE_SITE_NAME": "My Site Name" } }.

La propiedad System.Configuration.ConfigurationManager.AppSettings es una API alternativa para obtener los valores de configuración de la aplicación, pero se recomienda que use GetEnvironmentVariable como se muestra aquí.

Enlace en tiempo de ejecución

En C# y otros lenguajes .NET, puede usar un patrón de enlace imperativo, en contraposición a los enlaces declarativos de los atributos. Los enlaces imperativos resultan útiles cuando los parámetros de enlace tienen que calcularse en tiempo de ejecución, en lugar de en el tiempo de diseño. Con este patrón, se pueden establecer enlaces compatibles de entrada y salida sobre la marcha en el código de función.

Defina un enlace imperativo como se indica a continuación:

  • No incluya un atributo en la signatura de función para los enlaces imperativos deseados.

  • Pase un parámetro de entrada Binder binder o IBinder binder.

  • Utilice el siguiente patrón de C# para realizar el enlace de datos.

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

    BindingTypeAttribute es el atributo de .NET que define el enlace y T es un tipo de entrada o de salida compatible con ese tipo de enlace. T no puede ser un tipo de parámetro out (como out JObject). Por ejemplo, el enlace de salida de la tabla de Mobile Apps admite seis tipos de salida, pero solo se puede utilizar ICollector<T> o IAsyncCollector<T> con el enlace imperativo.

Ejemplo de un único atributo

El ejemplo de código siguiente crea un enlace de salida al blob de almacenamiento con la ruta de acceso al blob definida en tiempo de ejecución y, a continuación, escribe una cadena en el 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 define el enlace de entrada o salida del blob de almacenamiento, y TextWriter es un tipo de enlace de salida admitido.

Ejemplo de varios atributos

En el ejemplo anterior se obtiene el valor de la aplicación para la cadena de conexión en la cuenta de almacenamiento principal de la aplicación de función (que es AzureWebJobsStorage). Se puede especificar una configuración personalizada de la aplicación para utilizarla para la cuenta de almacenamiento agregando el atributo StorageAccountAttribute y pasando la matriz de atributos a BindAsync<T>(). Use un parámetro Binder, no IBinder. Por ejemplo:

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!!");
        }
    }
}

Desencadenadores y enlaces

En esta tabla se muestran los enlaces que son compatibles con las versiones principales del entorno en tiempo de ejecución de Azure Functions:

Tipo 1.x1 2.x y versiones posteriores2 Desencadenador Entrada Output
Blob Storage
Azure Cosmos DB
Azure Data Explorer
SQL de Azure
Dapr4
Event Grid
Event Hubs
HTTP y webhooks
IoT Hub
Kafka3
Mobile Apps
Centros de notificaciones
Queue Storage
Redis
RabbitMQ3
SendGrid
Service Bus
SignalR
Table storage
Temporizador
Twilio

Notas:

  1. La compatibilidad con la versión 1.x del entorno de ejecución de Azure Functions finalizará el 14 de septiembre de 2026. Se recomienda encarecidamente migrar las aplicaciones a la versión 4.x para obtener soporte completo.
  2. A partir del entorno de ejecución de la versión 2.x, se deben registrar todos los enlaces excepto HTTP y Timer. Consulte Registro de extensiones de enlace.
  3. Los desencadenadores no se admiten en el plan de consumo. Requiere desencadenadores controlados por el runtime.
  4. Solo se admite en Kubernetes, IoT Edge y otros modos autohospedados.

Pasos siguientes