Azure Functions 的 Azure 表存储绑定

本文介绍如何在 Azure Functions 中使用 Azure 表存储绑定。 Azure Functions 支持 Azure 表存储使用输入和输出绑定。

此参考信息面向 Azure Functions 开发人员。 Azure Functions 的新手请从以下资源入手:

包 - Functions 1.x

Microsoft.Azure.WebJobs NuGet 包 2.x 版中提供了表存储绑定。 azure-webjobs-sdk GitHub 存储库中提供了此包的源代码。

C# 类库

对于 C# 类库开发,自动在项目中安装该包。

Functions 2.x 中的绑定扩展

对于 Azure Functions 2.x 版中的本地开发,该包自动注册为绑定扩展

Functions 1.x 中的 Azure 存储 SDK 版本

在 Functions 1.x 中,存储触发器和绑定使用 7.2.1 版的 Azure 存储 SDK(WindowsAzure.Storage NuGet 包)。 如果引用另一版本的存储 SDK,而且在函数签名中绑定到某个存储 SDK 类型,则 Functions 运行时可能会报告它不能绑定到该类型。 此解决方案是为了确保项目引用 WindowsAzure.Storage 7.2.1

包 - Functions 2.x

Microsoft.Azure.WebJobs.Extensions.Storage NuGet 包 3.x 版中提供了表存储绑定。 azure-webjobs-sdk GitHub 存储库中提供了此包的源代码。

下表说明了如何在每个开发环境中添加对此绑定的支持。

开发环境 添加支持
Functions 2.x
本地开发 - C# 类库 安装包
本地开发 - C# 脚本、JavaScript、F#、Java 和 Python 注册扩展
门户开发 添加输出绑定时安装

若要了解如何更新门户中的现有绑定扩展而不必重新发布函数应用项目,请参阅更新扩展

输入

使用 Azure 表存储输入绑定读取 Azure 存储帐户中的表。

输入 - 示例

参阅语言特定的示例:

输入 - C# 示例 - 一个实体

以下示例演示读取单个表行的 C# 函数

行键值“{queueTrigger}”指示行键来自队列消息字符串。

public class TableStorage
{
    public class MyPoco
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Text { get; set; }
    }

    [FunctionName("TableInput")]
    public static void TableInput(
        [QueueTrigger("table-items")] string input, 
        [Table("MyTable", "MyPartition", "{queueTrigger}")] MyPoco poco, 
        ILogger log)
    {
        log.LogInformation($"PK={poco.PartitionKey}, RK={poco.RowKey}, Text={poco.Text}");
    }
}

输入 - C# 示例 - IQueryable

以下示例演示读取多个表行的 C# 函数。 请注意,MyPoco 类派生自 TableEntity

public class TableStorage
{
    public class MyPoco : TableEntity
    {
        public string Text { get; set; }
    }

    [FunctionName("TableInput")]
    public static void TableInput(
        [QueueTrigger("table-items")] string input, 
        [Table("MyTable", "MyPartition")] IQueryable<MyPoco> pocos, 
        ILogger log)
    {
        foreach (MyPoco poco in pocos)
        {
            log.LogInformation($"PK={poco.PartitionKey}, RK={poco.RowKey}, Text={poco.Text}");
        }
    }
}

输入 - C# 示例 - CloudTable

Functions v2 运行时不支持 IQueryable。 一种替代方法是使用 CloudTable 方法参数通过 Azure 存储 SDK 来读取表。 下面是一个查询 Azure Functions 日志表的 2.x 函数示例:

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Threading.Tasks;

namespace FunctionAppCloudTable2
{
    public class LogEntity : TableEntity
    {
        public string OriginalName { get; set; }
    }
    public static class CloudTableDemo
    {
        [FunctionName("CloudTableDemo")]
        public static async Task Run(
            [TimerTrigger("0 */1 * * * *")] TimerInfo myTimer, 
            [Table("AzureWebJobsHostLogscommon")] CloudTable cloudTable,
            ILogger log)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");

            TableQuery<LogEntity> rangeQuery = new TableQuery<LogEntity>().Where(
                TableQuery.CombineFilters(
                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, 
                        "FD2"),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThan, 
                        "t")));

            // Execute the query and loop through the results
            foreach (LogEntity entity in 
                await cloudTable.ExecuteQuerySegmentedAsync(rangeQuery, null))
            {
                log.LogInformation(
                    $"{entity.PartitionKey}\t{entity.RowKey}\t{entity.Timestamp}\t{entity.OriginalName}");
            }
        }
    }
}

有关如何使用 CloudTable 的详细信息,请参阅 Azure 表存储入门

如果在尝试绑定到 CloudTable 时出现错误消息,请确保引用正确的存储 SDK 版本

输入 - C# 脚本示例 - 一个实体

以下示例演示 function.json 文件中的一个表输入绑定以及使用该绑定的 C# 脚本代码。 该函数使用队列触发器来读取单个表行。

function.json 文件指定 partitionKeyrowKeyrowKey 值“{queueTrigger}”指示行键来自队列消息字符串。

{
  "bindings": [
    {
      "queueName": "myqueue-items",
      "connection": "MyStorageConnectionAppSetting",
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in"
    },
    {
      "name": "personEntity",
      "type": "table",
      "tableName": "Person",
      "partitionKey": "Test",
      "rowKey": "{queueTrigger}",
      "connection": "MyStorageConnectionAppSetting",
      "direction": "in"
    }
  ],
  "disabled": false
}

配置部分解释了这些属性。

C# 脚本代码如下所示:

public static void Run(string myQueueItem, Person personEntity, ILogger log)
{
    log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
    log.LogInformation($"Name in Person entity: {personEntity.Name}");
}

public class Person
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public string Name { get; set; }
}

输入 - C# 脚本示例 - IQueryable

以下示例演示 function.json 文件中的一个表输入绑定以及使用该绑定的 C# 脚本代码。 该函数读取队列消息中指定的分区键的实体。

function.json 文件如下所示:

{
  "bindings": [
    {
      "queueName": "myqueue-items",
      "connection": "MyStorageConnectionAppSetting",
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in"
    },
    {
      "name": "tableBinding",
      "type": "table",
      "connection": "MyStorageConnectionAppSetting",
      "tableName": "Person",
      "direction": "in"
    }
  ],
  "disabled": false
}

配置部分解释了这些属性。

C# 脚本代码添加对 Azure 存储 SDK 的引用,以便实体类型可以从 TableEntity 派生。

#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.Extensions.Logging;

public static void Run(string myQueueItem, IQueryable<Person> tableBinding, ILogger log)
{
    log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
    foreach (Person person in tableBinding.Where(p => p.PartitionKey == myQueueItem).ToList())
    {
        log.LogInformation($"Name: {person.Name}");
    }
}

public class Person : TableEntity
{
    public string Name { get; set; }
}

输入 - C# 脚本示例 - CloudTable

Functions v2 运行时不支持 IQueryable。 一种替代方法是使用 CloudTable 方法参数通过 Azure 存储 SDK 来读取表。 下面是一个查询 Azure Functions 日志表的 2.x 函数示例:

{
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */1 * * * *"
    },
    {
      "name": "cloudTable",
      "type": "table",
      "connection": "AzureWebJobsStorage",
      "tableName": "AzureWebJobsHostLogscommon",
      "direction": "in"
    }
  ],
  "disabled": false
}
#r "Microsoft.WindowsAzure.Storage"
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public static async Task Run(TimerInfo myTimer, CloudTable cloudTable, ILogger log)
{
    log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");

    TableQuery<LogEntity> rangeQuery = new TableQuery<LogEntity>().Where(
    TableQuery.CombineFilters(
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, 
            "FD2"),
        TableOperators.And,
        TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThan, 
            "a")));

    // Execute the query and loop through the results
    foreach (LogEntity entity in 
    await cloudTable.ExecuteQuerySegmentedAsync(rangeQuery, null))
    {
        log.LogInformation(
            $"{entity.PartitionKey}\t{entity.RowKey}\t{entity.Timestamp}\t{entity.OriginalName}");
    }
}

public class LogEntity : TableEntity
{
    public string OriginalName { get; set; }
}

有关如何使用 CloudTable 的详细信息,请参阅 Azure 表存储入门

如果在尝试绑定到 CloudTable 时出现错误消息,请确保引用正确的存储 SDK 版本

输入 - F# 示例

以下示例演示 function.json 文件中的一个表输入绑定以及使用该绑定的 F# 脚本代码。 该函数使用队列触发器来读取单个表行。

function.json 文件指定 partitionKeyrowKeyrowKey 值“{queueTrigger}”指示行键来自队列消息字符串。

{
  "bindings": [
    {
      "queueName": "myqueue-items",
      "connection": "MyStorageConnectionAppSetting",
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in"
    },
    {
      "name": "personEntity",
      "type": "table",
      "tableName": "Person",
      "partitionKey": "Test",
      "rowKey": "{queueTrigger}",
      "connection": "MyStorageConnectionAppSetting",
      "direction": "in"
    }
  ],
  "disabled": false
}

配置部分解释了这些属性。

F# 代码如下所示:

[<CLIMutable>]
type Person = {
  PartitionKey: string
  RowKey: string
  Name: string
}

let Run(myQueueItem: string, personEntity: Person) =
    log.LogInformation(sprintf "F# Queue trigger function processed: %s" myQueueItem)
    log.LogInformation(sprintf "Name in Person entity: %s" personEntity.Name)

输入 - JavaScript 示例

以下示例演示 function.json 文件中的一个表输入绑定以及使用该绑定的 JavaScript 脚本代码。 该函数使用队列触发器来读取单个表行。

function.json 文件指定 partitionKeyrowKeyrowKey 值“{queueTrigger}”指示行键来自队列消息字符串。

{
  "bindings": [
    {
      "queueName": "myqueue-items",
      "connection": "MyStorageConnectionAppSetting",
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in"
    },
    {
      "name": "personEntity",
      "type": "table",
      "tableName": "Person",
      "partitionKey": "Test",
      "rowKey": "{queueTrigger}",
      "connection": "MyStorageConnectionAppSetting",
      "direction": "in"
    }
  ],
  "disabled": false
}

配置部分解释了这些属性。

JavaScript 代码如下所示:

module.exports = function (context, myQueueItem) {
    context.log('Node.js queue trigger function processed work item', myQueueItem);
    context.log('Person entity name: ' + context.bindings.personEntity.Name);
    context.done();
};

输入 - Java 示例

以下示例显示了 HTTP 触发的函数,该函数返回表存储中指定分区中项的总计数。

@FunctionName("getallcount")
public int run(
   @HttpTrigger(name = "req",
                 methods = {"get"},
                 authLevel = AuthorizationLevel.ANONYMOUS) Object dummyShouldNotBeUsed,
   @TableInput(name = "items",
                tableName = "mytablename",  partitionKey = "myparkey",
                connection = "myconnvarname") MyItem[] items
) {
    return items.length;
}

输入 - 特性

C# 类库中,请使用以下属性来配置表输入绑定:

  • TableAttribute

    该特性的构造函数采用表名称、分区键和行键。 可对函数的 out 参数或返回值使用该特性,如以下示例中所示:

    [FunctionName("TableInput")]
    public static void Run(
        [QueueTrigger("table-items")] string input, 
        [Table("MyTable", "Http", "{queueTrigger}")] MyPoco poco, 
        ILogger log)
    {
        ...
    }
    

    可以设置 Connection 属性来指定要使用的存储帐户,如以下示例中所示:

    [FunctionName("TableInput")]
    public static void Run(
        [QueueTrigger("table-items")] string input, 
        [Table("MyTable", "Http", "{queueTrigger}", Connection = "StorageConnectionAppSetting")] MyPoco poco, 
        ILogger log)
    {
        ...
    }
    

    有关完整示例,请参阅输入 - C# 示例

  • StorageAccountAttribute

    提供另一种方式来指定要使用的存储帐户。 构造函数采用包含存储连接字符串的应用设置的名称。 可以在参数、方法或类级别应用该特性。 以下示例演示类级别和方法级别:

    [StorageAccount("ClassLevelStorageAppSetting")]
    public static class AzureFunctions
    {
        [FunctionName("TableInput")]
        [StorageAccount("FunctionLevelStorageAppSetting")]
        public static void Run( //...
    {
        ...
    }
    

要使用的存储帐户按以下顺序确定:

  • Table 特性的 Connection 属性。
  • 作为 Table 特性应用到同一参数的 StorageAccount 特性。
  • 应用到函数的 StorageAccount 特性。
  • 应用到类的 StorageAccount 特性。
  • 函数应用的默认存储帐户(“AzureWebJobsStorage”应用设置)。

输入 - Java 注释

Java 函数运行时库中,对其值将来自表存储的参数使用 @TableInput 注释。 可以将此注释与本机 Java 类型、POJO 或使用了 Optional 的可为 null 的值一起使用。

输入 - 配置

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

function.json 属性 Attribute 属性 说明
类型 不适用 必须设置为 table。 在 Azure 门户中创建绑定时,会自动设置此属性。
direction 不适用 必须设置为 in。 在 Azure 门户中创建绑定时,会自动设置此属性。
name 不适用 表示函数代码中的表或实体的变量的名称。
tableName TableName 表的名称。
partitionKey PartitionKey 可选。 要读取的表实体的分区键。 有关如何使用此属性的指导,请参阅用法部分。
rowKey RowKey 可选。 要读取的表实体的行键。 有关如何使用此属性的指导,请参阅用法部分。
take Take 可选。 要在 JavaScript 中读取的最大实体数。 有关如何使用此属性的指导,请参阅用法部分。
filter Filter 可选。 JavaScript 中的表输入的 OData 筛选表达式。 有关如何使用此属性的指导,请参阅用法部分。
连接 Connection 包含要用于此绑定的存储连接字符串的应用设置的名称。 如果应用设置名称以“AzureWebJobs”开始,则只能在此处指定该名称的余下部分。 例如,如果将 connection 设置为“MyStorage”,函数运行时将会查找名为“AzureWebJobsMyStorage”的应用设置。 如果将 connection 留空,函数运行时将使用名为 AzureWebJobsStorage 的应用设置中的默认存储连接字符串。

在本地进行开发时,应用设置将取 local.settings.json 文件的值。

输入 - 用法

表存储输入绑定支持以下方案:

  • 在 C# 或 C# 脚本中读取一行

    设置 partitionKeyrowKey。 使用方法参数 T <paramName> 访问表数据。 在 C# 脚本中,paramName 是在 function.jsonname 属性中指定的值。 T 通常是实现 ITableEntity 或派生自 TableEntity 的类型。 此方案中不使用 filtertake 属性。

  • 在 C# 或 C# 脚本中读取一行或多行

    使用方法参数 IQueryable<T> <paramName> 访问表数据。 在 C# 脚本中,paramName 是在 function.jsonname 属性中指定的值。 T 必须是实现 ITableEntity 或派生自 TableEntity 的类型。 可以使用 IQueryable 方法执行任何所需的筛选。 此方案中不使用 partitionKeyrowKeyfiltertake 属性。

    Note

    Functions v2 运行时不支持 IQueryable。 一种替代方法是使用 CloudTable paramName 方法参数通过 Azure 存储 SDK 来读取表。 如果在尝试绑定到 CloudTable 时出现错误消息,请确保引用正确的存储 SDK 版本

  • 在 JavaScript 中读取一行或多行

    设置 filtertake 属性。 不要设置 partitionKeyrowKey。 使用 context.bindings.<name> 访问输入一个或多个输入表实体。 反序列化的对象具有 RowKeyPartitionKey 属性。

输出

使用 Azure 表存储输出绑定读取将实体写入 Azure 存储帐户中的表。

Note

此输出绑定不支持更新现有实体。 请使用 Azure 存储 SDK 中的 TableOperation.Replace 操作来更新现有实体。

输出 - 示例

参阅语言特定的示例:

输出 - C# 示例

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

public class TableStorage
{
    public class MyPoco
    {
        public string PartitionKey { get; set; }
        public string RowKey { get; set; }
        public string Text { get; set; }
    }

    [FunctionName("TableOutput")]
    [return: Table("MyTable")]
    public static MyPoco TableOutput([HttpTrigger] dynamic input, ILogger log)
    {
        log.LogInformation($"C# http trigger function processed: {input.Text}");
        return new MyPoco { PartitionKey = "Http", RowKey = Guid.NewGuid().ToString(), Text = input.Text };
    }
}

输出 - C# 脚本示例

以下示例演示 function.json 文件中的一个表输出绑定以及使用该绑定的 C# 脚本代码。 该函数写入多个表实体。

function.json 文件如下所示:

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

配置部分解释了这些属性。

C# 脚本代码如下所示:

public static void Run(string input, ICollector<Person> tableBinding, ILogger log)
{
    for (int i = 1; i < 10; i++)
        {
            log.LogInformation($"Adding Person entity {i}");
            tableBinding.Add(
                new Person() { 
                    PartitionKey = "Test", 
                    RowKey = i.ToString(), 
                    Name = "Name" + i.ToString() }
                );
        }

}

public class Person
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public string Name { get; set; }
}

输出 - F# 示例

以下示例演示 function.json 文件中的一个表输出绑定以及使用该绑定的 F# 脚本代码。 该函数写入多个表实体。

function.json 文件如下所示:

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

配置部分解释了这些属性。

F# 代码如下所示:

[<CLIMutable>]
type Person = {
  PartitionKey: string
  RowKey: string
  Name: string
}

let Run(input: string, tableBinding: ICollector<Person>, log: ILogger) =
    for i = 1 to 10 do
        log.LogInformation(sprintf "Adding Person entity %d" i)
        tableBinding.Add(
            { PartitionKey = "Test"
              RowKey = i.ToString()
              Name = "Name" + i.ToString() })

输出 - JavaScript 示例

以下示例演示 function.json 文件中的一个表输出绑定以及使用该绑定的 JavaScript 函数。 该函数写入多个表实体。

function.json 文件如下所示:

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

配置部分解释了这些属性。

JavaScript 代码如下所示:

module.exports = function (context) {

    context.bindings.tableBinding = [];

    for (var i = 1; i < 10; i++) {
        context.bindings.tableBinding.push({
            PartitionKey: "Test",
            RowKey: i.toString(),
            Name: "Name " + i
        });
    }

    context.done();
};

输出 - 特性

C# 类库中,使用 TableAttribute

该特性的构造函数采用表名称。 可对函数的 out 参数或返回值使用该特性,如以下示例中所示:

[FunctionName("TableOutput")]
[return: Table("MyTable")]
public static MyPoco TableOutput(
    [HttpTrigger] dynamic input, 
    ILogger log)
{
    ...
}

可以设置 Connection 属性来指定要使用的存储帐户,如以下示例中所示:

[FunctionName("TableOutput")]
[return: Table("MyTable", Connection = "StorageConnectionAppSetting")]
public static MyPoco TableOutput(
    [HttpTrigger] dynamic input, 
    ILogger log)
{
    ...
}

有关完整示例,请参阅输出 - C# 示例

可以使用 StorageAccount 特性在类、方法或参数级别指定存储帐户。 有关详细信息,请参阅输入 - 特性

输出 - 配置

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

function.json 属性 Attribute 属性 说明
类型 不适用 必须设置为 table。 在 Azure 门户中创建绑定时,会自动设置此属性。
direction 不适用 必须设置为 out。 在 Azure 门户中创建绑定时,会自动设置此属性。
name 不适用 在函数代码中使用的、表示表或实体的变量名称。 设置为 $return 可引用函数返回值。
tableName TableName 表的名称。
partitionKey PartitionKey 要写入的表实体的分区键。 有关如何使用此属性的指导,请参阅用法部分
rowKey RowKey 要写入的表实体的行键。 有关如何使用此属性的指导,请参阅用法部分
连接 Connection 包含要用于此绑定的存储连接字符串的应用设置的名称。 如果应用设置名称以“AzureWebJobs”开始,则只能在此处指定该名称的余下部分。 例如,如果将 connection 设置为“MyStorage”,函数运行时将会查找名为“AzureWebJobsMyStorage”的应用设置。 如果将 connection 留空,函数运行时将使用名为 AzureWebJobsStorage 的应用设置中的默认存储连接字符串。

在本地进行开发时,应用设置将取 local.settings.json 文件的值。

输出 - 用法

表存储输出绑定支持以下方案:

  • 以任何语言编写一行

    在 C# 和 C# 脚本中,可以使用 out T paramName 等方法参数或函数返回值访问输出表实体。 在 C# 脚本中,paramName 是在 function.jsonname 属性中指定的值。 如果 function.json 文件或 Table 特性提供了分区键和行键,则 T 可以是任何可序列化类型。 否则,T 必须是包含 PartitionKeyRowKey 属性的类型。 在此方案中,T 通常实现 ITableEntity 或派生自 TableEntity,但不一定非要这样。

  • 在 C# 或 C# 脚本中写入一行或多行

    在 C# 和 C# 脚本中,可以使用方法参数 ICollector<T> paramNameIAsyncCollector<T> paramName 访问输出表实体。 在 C# 脚本中,paramName 是在 function.jsonname 属性中指定的值。 T 指定要添加的实体的架构。 通常,T 派生自 TableEntity 或实现 ITableEntity,但不一定非要这样。 此方案不使用 function.json 中的分区键和行键值,也不使用 Table 特性构造函数。

    一种替代方法是使用 CloudTable 方法参数通过 Azure 存储 SDK 来写入表。 如果在尝试绑定到 CloudTable 时出现错误消息,请确保引用正确的存储 SDK 版本。 有关绑定到 CloudTable 的代码的示例,请参阅本文前面的 C#C# 脚本的输入绑定示例。

  • 在 JavaScript 中写入一行或多行

    在 JavaScript 函数中,可以使用 context.bindings.<name> 访问表输出。

异常和返回代码

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

后续步骤