你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

适用于 Azure Functions 的 Azure 表输出绑定

使用 Azure 表输出绑定将实体写入 Azure Cosmos DB for TableAzure 表存储中的表。

有关设置和配置的详细信息,请参阅概述

注意

此输出绑定仅支持在表中创建新实体。 如果需要从函数代码更新现有实体,请改为直接使用 Azure 表 SDK。

重要

本文使用选项卡来支持多个版本的 Node.js 编程模型。 v4 模型已正式发布,旨在为 JavaScript 和 TypeScript 开发人员提供更为灵活和直观的体验。 有关 v4 模型工作原理的更多详细信息,请参阅 Azure Functions Node.js 开发人员指南。 要详细了解 v3 和 v4 之间的差异,请参阅迁移指南

示例

可使用以下 C# 模式之一来创建 C# 函数:

  • 独立辅助角色模型:编译的 C# 函数,该函数在独立于运行时的工作进程中运行。 需要独立工作进程才能支持在 LTS 和非 LTS 版 .NET 和 .NET Framework 上运行的 C# 函数。 独立工作进程函数的扩展使用 Microsoft.Azure.Functions.Worker.Extensions.* 命名空间。
  • 进程内模型:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。 在此模型的变体中,可以使用 C# 脚本运行 Functions,该脚本主要用于 C# 门户编辑。 进程内函数的扩展使用 Microsoft.Azure.WebJobs.Extensions.* 命名空间。

下面的 MyTableData 类表示表中的一行数据:

public class MyTableData : Azure.Data.Tables.ITableEntity
{
    public string Text { get; set; }

    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public DateTimeOffset? Timestamp { get; set; }
    public ETag ETag { get; set; }
}

以下函数由队列存储触发器启动,可将新 MyDataTable 实体写入名为 OutputTable 的表。

[Function("TableFunction")]
[TableOutput("OutputTable", Connection = "AzureWebJobsStorage")]
public static MyTableData Run(
    [QueueTrigger("table-items")] string input,
    [TableInput("MyTable", "<PartitionKey>", "{queueTrigger}")] MyTableData tableInput,
    FunctionContext context)
{
    var logger = context.GetLogger("TableFunction");

    logger.LogInformation($"PK={tableInput.PartitionKey}, RK={tableInput.RowKey}, Text={tableInput.Text}");

    return new MyTableData()
    {
        PartitionKey = "queue",
        RowKey = Guid.NewGuid().ToString(),
        Text = $"Output record with rowkey {input} created at {DateTime.Now}"
    };
}

以下示例演示了使用 HTTP 触发器写入单个表行的 Java 函数。

public class Person {
    private String PartitionKey;
    private String RowKey;
    private String Name;

    public String getPartitionKey() {return this.PartitionKey;}
    public void setPartitionKey(String key) {this.PartitionKey = key; }
    public String getRowKey() {return this.RowKey;}
    public void setRowKey(String key) {this.RowKey = key; }
    public String getName() {return this.Name;}
    public void setName(String name) {this.Name = name; }
}

public class AddPerson {

    @FunctionName("addPerson")
    public HttpResponseMessage get(
            @HttpTrigger(name = "postPerson", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION, route="persons/{partitionKey}/{rowKey}") HttpRequestMessage<Optional<Person>> request,
            @BindingName("partitionKey") String partitionKey,
            @BindingName("rowKey") String rowKey,
            @TableOutput(name="person", partitionKey="{partitionKey}", rowKey = "{rowKey}", tableName="%MyTableName%", connection="MyConnectionString") OutputBinding<Person> person,
            final ExecutionContext context) {

        Person outPerson = new Person();
        outPerson.setPartitionKey(partitionKey);
        outPerson.setRowKey(rowKey);
        outPerson.setName(request.getBody().get().getName());

        person.setValue(outPerson);

        return request.createResponseBuilder(HttpStatus.OK)
                        .header("Content-Type", "application/json")
                        .body(outPerson)
                        .build();
    }
}

以下示例演示了使用 HTTP 触发器写入多个表行的 Java 函数。

public class Person {
    private String PartitionKey;
    private String RowKey;
    private String Name;

    public String getPartitionKey() {return this.PartitionKey;}
    public void setPartitionKey(String key) {this.PartitionKey = key; }
    public String getRowKey() {return this.RowKey;}
    public void setRowKey(String key) {this.RowKey = key; }
    public String getName() {return this.Name;}
    public void setName(String name) {this.Name = name; }
}

public class AddPersons {

    @FunctionName("addPersons")
    public HttpResponseMessage get(
            @HttpTrigger(name = "postPersons", methods = {HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION, route="persons/") HttpRequestMessage<Optional<Person[]>> request,
            @TableOutput(name="person", tableName="%MyTableName%", connection="MyConnectionString") OutputBinding<Person[]> persons,
            final ExecutionContext context) {

        persons.setValue(request.getBody().get());

        return request.createResponseBuilder(HttpStatus.OK)
                        .header("Content-Type", "application/json")
                        .body(request.getBody().get())
                        .build();
    }
}

以下示例显示写入多个表实体的表输出绑定。

import { app, HttpRequest, HttpResponseInit, InvocationContext, output } from '@azure/functions';

const tableOutput = output.table({
    tableName: 'Person',
    connection: 'MyStorageConnectionAppSetting',
});

interface PersonEntity {
    PartitionKey: string;
    RowKey: string;
    Name: string;
}

export async function httpTrigger1(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
    const rows: PersonEntity[] = [];
    for (let i = 1; i < 10; i++) {
        rows.push({
            PartitionKey: 'Test',
            RowKey: i.toString(),
            Name: `Name ${i}`,
        });
    }
    context.extraOutputs.set(tableOutput, rows);
    return { status: 201 };
}

app.http('httpTrigger1', {
    methods: ['POST'],
    authLevel: 'anonymous',
    extraOutputs: [tableOutput],
    handler: httpTrigger1,
});
const { app, output } = require('@azure/functions');

const tableOutput = output.table({
    tableName: 'Person',
    connection: 'MyStorageConnectionAppSetting',
});

app.http('httpTrigger1', {
    methods: ['POST'],
    authLevel: 'anonymous',
    extraOutputs: [tableOutput],
    handler: async (request, context) => {
        const rows = [];
        for (let i = 1; i < 10; i++) {
            rows.push({
                PartitionKey: 'Test',
                RowKey: i.toString(),
                Name: `Name ${i}`,
            });
        }
        context.extraOutputs.set(tableOutput, rows);
        return { status: 201 };
    },
});

以下示例演示如何通过一个函数向表中写入多个实体。

function.json 中的绑定配置:

{
  "bindings": [
    {
      "name": "InputData",
      "type": "manualTrigger",
      "direction": "in"
    },
    {
      "tableName": "Person",
      "connection": "MyStorageConnectionAppSetting",
      "name": "TableBinding",
      "type": "table",
      "direction": "out"
    }
  ],
  "disabled": false
}

run.ps1 中的 PowerShell 代码:

param($InputData, $TriggerMetadata)

foreach ($i in 1..10) {
    Push-OutputBinding -Name TableBinding -Value @{
        PartitionKey = 'Test'
        RowKey = "$i"
        Name = "Name $i"
    }
}

下面的示例演示如何使用表存储输出绑定。 通过为 nametableNamepartitionKeyconnection 分配值,在 function.json 中配置 table 绑定:

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "message",
      "type": "table",
      "tableName": "messages",
      "partitionKey": "message",
      "connection": "AzureWebJobsStorage",
      "direction": "out"
    },
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

以下函数为 rowKey 值生成唯一 UUI,并将消息持久保存在表存储中。

import logging
import uuid
import json

import azure.functions as func

def main(req: func.HttpRequest, message: func.Out[str]) -> func.HttpResponse:

    rowKey = str(uuid.uuid4())

    data = {
        "Name": "Output binding message",
        "PartitionKey": "message",
        "RowKey": rowKey
    }

    message.set(json.dumps(data))

    return func.HttpResponse(f"Message created with the rowKey: {rowKey}")

特性

进程内独立工作进程 C# 库使用特性来定义函数。 C# 脚本改用 function.json 配置文件,如 C# 脚本指南中所述。

C# 类库中,TableInputAttribute 支持以下属性:

Attribute 属性 说明
TableName 要向其写入的表的名称。
PartitionKey 要写入的表实体的分区键。
RowKey 要写入的表实体的行键。
Connection 指定如何连接到表服务的应用设置或设置集合的名称。 请参阅连接

批注

Java 函数运行时库中,对参数使用 TableOutput 注释以将值写入到表中。 该特性支持以下元素:

元素 说明
name 在函数代码中使用的、表示表或实体的变量名称。
dataType 定义 Functions 运行时应如何处理参数值。 有关详细信息,请参阅 dataType
tableName 要向其写入的表的名称。
partitionKey 要写入的表实体的分区键。
rowKey 要写入的表实体的行键。
连接 指定如何连接到表服务的应用设置或设置集合的名称。 请参阅连接

配置

下表说明了可以在传递给 output.table() 方法的 options 对象上设置的属性。

properties 说明
tableName 要向其写入的表的名称。
partitionKey 要写入的表实体的分区键。
rowKey 要写入的表实体的行键。
连接 指定如何连接到表服务的应用设置或设置集合的名称。 请参阅连接

配置

下表解释了在 function.json 文件中设置的绑定配置属性。

“function.json”属性 说明
type 必须设置为 table。 在 Azure 门户中创建绑定时,会自动设置此属性。
direction 必须设置为 out。 在 Azure 门户中创建绑定时,会自动设置此属性。
name 在函数代码中使用的、表示表或实体的变量名称。 设置为 $return 可引用函数返回值。
tableName 要向其写入的表的名称。
partitionKey 要写入的表实体的分区键。
rowKey 要写入的表实体的行键。
连接 指定如何连接到表服务的应用设置或设置集合的名称。 请参阅连接

在本地开发时,请将应用程序设置添加到 Values 集合的 local.settings.json 文件

连接

connection 属性是对环境配置的引用,它指定应用应该如何连接到表服务。 它可能指定:

如果配置的值既是单个设置的完全匹配,也是其他设置的前缀匹配,则使用完全匹配。

连接字符串

若要在 Azure 表存储中为表获取连接字符串,请执行管理存储帐户访问密钥中显示的步骤。 若要在 Azure Cosmos DB for Table 中为表获取连接字符串,请执行 Azure Cosmos DB for Table 常见问题解答中显示的步骤。

此连接字符串应存储在应用程序设置中,其名称与绑定配置的 connection 属性指定的值匹配。

如果应用设置名称以“AzureWebJobs”开始,则只能在此处指定该名称的余下部分。 例如,如果将 connection 设置为“MyStorage”,则 Functions 运行时会查找名为“AzureWebJobsMyStorage”的应用设置。 如果将 connection 留空,函数运行时将使用名为 AzureWebJobsStorage 的应用设置中的默认存储连接字符串。

基于标识的连接

如果使用的是表 API 扩展,而不是将连接字符串与机密一起使用,则可以将应用设置为使用 Microsoft Entra 标识。 这仅适用于访问 Azure 存储中的表的情况。 要使用标识,需要定义公共前缀下的设置,该前缀映射到触发器和绑定配置中的 connection 属性。

如果将 connection 设置为“AzureWebJobsStorage”,请参阅使用标识连接到主机存储。 对于所有其他连接,扩展需要以下属性:

属性 环境变量模板 说明 示例值
表服务 URI <CONNECTION_NAME_PREFIX>__tableServiceUri1 使用 HTTPS 方案时,要连接到的 Azure 存储表服务的数据平面 URI。 https://<storage_account_name>.table.core.windows.net

1 <CONNECTION_NAME_PREFIX>__serviceUri 可以用作别名。 如果同时提供了两个窗体,则会使用 tableServiceUri 窗体。 如果要跨 Blob、队列和/或表使用总体连接配置,则无法使用 serviceUri 窗体。

可以设置其他属性来自定义连接。 请参阅基于标识的连接的通用属性

如果要在 Azure 存储中跨 Blob、队列和/或表使用总体连接配置,则无法使用 serviceUri 窗体。 URI 只能指定表服务。 作为一种替代方案,可以在同一前缀下为每个服务专门提供一个 URI,从而允许使用单个连接。

在 Azure Functions 服务中托管时,基于标识的连接将使用托管标识。 默认情况下使用系统分配的标识,但可以使用 credentialclientID 属性来指定用户分配的标识。 请注意,不支持为用户分配的标识配置资源 ID。 在其他上下文(如本地开发)中运行时,将改用开发人员标识,尽管可以进行自定义。 请参阅使用基于标识的连接进行本地开发

向标识授予权限

无论使用何种标识,都必须具有执行所需操作的权限。 对于大多数 Azure 服务,这意味着你需要使用内置角色或者提供这些权限的自定义角色在 Azure RBAC 中分配角色

重要

某些权限可能由并非所有上下文都需要的目标服务公开。 尽可能遵循最低权限原则,仅授予标识所需的权限。 例如,如果应用只需要从数据源进行读取即可,则使用仅具有读取权限的角色。 分配一个也具有该服务写入权限的角色并不恰当,因为对于读取操作来说,写入是多余的权限。 同样,你也希望确保角色分配的范围仅限于需要读取的资源。

你将需要创建一个角色分配,以便在运行时提供对 Azure 存储表服务的访问权限。 所有者等管理角色还不够。 下表显示了在正常操作中对 Azure 存储使用 Azure 表扩展时建议使用的内置角色。 根据所编写的代码,应用程序可能需要具有其他权限。

绑定类型 内置角色示例(Azure 存储1
输入绑定 存储表数据读取者
输出绑定 存储表数据参与者

1 如果应用改为连接到 Azure Cosmos DB for Table 中的表,则不支持使用标识,并且连接必须使用连接字符串。

使用情况

绑定的用法取决于扩展包版本,以及函数应用中使用的 C# 形式,可以是以下形式之一:

独立工作进程类库的已编译 C# 函数在独立于运行时的进程中运行。

选择一个版本以查看模式和版本的使用情况详细信息。

如果希望函数写入单个实体,Azure 表输出绑定可以绑定到以下类型:

类型 说明
实现 [ITableEntity] 的 JSON 可序列化类型 函数尝试将普通的旧 CLR 对象 (POCO) 类型序列化为实体。 该类型必须实现 [ITableEntity] 或具有字符串 RowKey 属性和字符串 PartitionKey 属性。

如果希望函数写入多个实体,Azure 表输出绑定可以绑定到以下类型:

类型 说明
T[],其中 T 是单个实体类型之一 包含多个实体的数组。 每个条目表示一个实体。

对于其他输出方案,请直接从 Azure.Data.Tables 创建和使用 TableClient 和其他类型的 。 有关使用依赖项注入从 Azure SDK 创建客户端类型的示例,请参阅 “注册 Azure 客户端 ”。

有两个选项可用于使用 TableStorageOutput 注释从函数来输出表存储行:

选项 描述
返回值 通过将注释应用于函数本身,函数的返回值将持久保存为表存储行。
命令性 若要显式设置表行,请将注释应用于 OutputBinding<T> 类型的特定参数,其中 T 包括 PartitionKeyRowKey 属性。 可以通过实现 ITableEntity 或继承 TableEntity 来随附这些属性。

通过返回值或使用 context.extraOutputs.set() 设置输出行数据。

若要写入到表数据,请使用 Push-OutputBinding cmdlet,将 -Name TableBinding 参数和 -Value 参数设置为等于行数据。 有关更多详细信息,请参阅 PowerShell 示例

有两个选项可用于从函数输出表存储行消息:

选项 描述
返回值 将 function.json 中的 name 属性 设置为 $return。 使用此配置时,函数的返回值将作为表存储行保留。
命令性 将值传递给声明为 Out 类型的参数的 set 方法。 传递给 set 的值将保留为表行。

有关特定使用情况的详细信息,请参阅示例

异常和返回代码

绑定 参考
表错误代码
Blob、表、队列 存储错误代码
Blob、表、队列 故障排除

后续步骤