如何配合使用 C++ 和 Azure 表存储

Tip

本文中的内容适用于 Azure 表存储。

概述

本指南将演示如何使用 Azure 表存储服务执行常见方案。 示例采用 C++ 编写,并使用了适用于 C++ 的 Azure 存储客户端库。 涉及的方案包括创建和删除表以及使用表实体

Note

本指南主要面向适用于 C++ 的 Azure 存储客户端库 1.0.0 版及更高版本。 推荐版本:存储客户端库 2.2.0(可通过 NuGetGitHub 获得)。

创建 Azure 服务帐户

可以通过 Azure 表存储使用表。 需要为所要使用的服务创建一个帐户。

创建 Azure 存储帐户

创建第一个 Azure 存储帐户的最简单方法是使用 Azure 门户。 若要了解更多信息,请参阅 创建存储帐户

也可以使用 Azure PowerShellAzure CLI 创建 Azure 存储帐户。

如果暂时不想创建存储帐户,也可以使用 Azure 存储模拟器在本地环境中运行和测试代码。 有关详细信息,请参阅 使用 Azure 存储模拟器进行开发和测试

创建 C++ 应用程序

本指南会使用可在 C++ 应用程序内运行的存储功能。 为此,需要安装适用于 C++ 的 Azure 存储客户端库,并在 Azure 订阅中创建 Azure 存储帐户。

若要安装适用于 C++ 的 Azure 存储客户端库,可使用以下方法:

  • Linux: 按照适用于 C++ 的 Azure 存储客户端库自述文件页中提供的说明进行操作。
  • Windows: 在 Visual Studio 中,单击“工具”>“NuGet 包管理器”>“程序包管理器控制台”。 在 NuGet 包管理器控制台中,键入以下命令,并按 Enter。

    Install-Package wastorage

配置对表客户端库的访问权限

将以下 include 语句添加到要在其中使用 Azure 存储 API 访问表的 C++ 文件的顶部:

#include <was/storage_account.h>
#include <was/table.h>

Azure 存储客户端或 Cosmos DB 客户端使用连接字符串来存储用于访问数据管理服务的终结点和凭据。 运行客户端应用程序时,必须提供相应格式的存储连接字符串或 Azure Cosmos DB 连接字符串。

设置 Azure 存储连接字符串

使用 Azure 门户中列出的存储帐户的存储帐户名称和访问密钥作为 AccountName 和 AccountKey 值。 有关存储帐户和访问密钥的信息,请参阅关于 Azure 存储帐户。 此示例演示如何声明一个静态字段以保存 Azure 存储连接字符串:

// Define the Storage connection string with your values.
const utility::string_t storage_connection_string(U("DefaultEndpointsProtocol=https;AccountName=your_storage_account;AccountKey=your_storage_account_key;EndpointSuffix=core.chinacloudapi.cn"));

以下示例假设已使用此方法获取存储连接字符串。

检索连接字符串

可使用 cloud_storage_account 类来表示存储帐户信息。 要从存储连接字符串中检索存储帐户信息,可以使用 parse 方法。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

接下来,获取对 cloud_table_client 类的引用,因为使用它可以获取表存储服务中存储的表和实体的引用对象。 以下代码使用我们在上面检索到的存储帐户对象创建 cloud_table_client 对象:

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

创建表

使用 cloud_table_client 对象可获得表和实体的引用对象。 以下代码将创建 cloud_table_client 对象并使用它创建新表。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);  

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Retrieve a reference to a table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Create the table if it doesn't exist.
table.create_if_not_exists();  

将实体添加到表

若要将实体添加到表,请创建一个新的 table_entity 对象并将其传递到 table_operation::insert_entity。 以下代码使用客户的名字作为行键,并使用姓氏作为分区键。 条目的分区键和行键共同唯一地标识表中的条目。 查询分区键相同的条目的速度快于查询分区键不同的条目的速度,但使用不同的分区键可实现更高的并行操作可伸缩性。 有关详细信息,请参阅 Azure 存储性能和可伸缩性核对清单

下面的代码创建一个 table_entity 新实例,其中包含要进行存储的部分客户数据。 接下来,该代码调用 table_operation::insert_entity 来创建一个 table_operation 对象,以便将实体插入表中,并将新的表实体与之关联。 最后,该代码调用 cloud_table 对象的 execute 方法。 并且新的 table_operation 向表服务发送请求,以此将新的客户实体插入“people”表中。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Retrieve a reference to a table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Create the table if it doesn't exist.
table.create_if_not_exists();

// Create a new customer entity.
azure::storage::table_entity customer1(U("Harp"), U("Walter"));

azure::storage::table_entity::properties_type& properties = customer1.properties();
properties.reserve(2);
properties[U("Email")] = azure::storage::entity_property(U("Walter@contoso.com"));

properties[U("Phone")] = azure::storage::entity_property(U("425-555-0101"));

// Create the table operation that inserts the customer entity.
azure::storage::table_operation insert_operation = azure::storage::table_operation::insert_entity(customer1);

// Execute the insert operation.
azure::storage::table_result insert_result = table.execute(insert_operation);

插入一批实体

可通过一个写入操作将一批条目插入到表服务。 以下代码创建一个 table_batch_operation 对象,并向其中添加三个插入操作。 每个插入操作的添加方法如下:创建一个新的实体对象,对其设置值,然后对 table_batch_operation 对象调用 insert 方法来将实体与新的插入操作相关联。 然后调用 cloud_table.execute 来执行此操作。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Define a batch operation.
azure::storage::table_batch_operation batch_operation;

// Create a customer entity and add it to the table.
azure::storage::table_entity customer1(U("Smith"), U("Jeff"));

azure::storage::table_entity::properties_type& properties1 = customer1.properties();
properties1.reserve(2);
properties1[U("Email")] = azure::storage::entity_property(U("Jeff@contoso.com"));
properties1[U("Phone")] = azure::storage::entity_property(U("425-555-0104"));

// Create another customer entity and add it to the table.
azure::storage::table_entity customer2(U("Smith"), U("Ben"));

azure::storage::table_entity::properties_type& properties2 = customer2.properties();
properties2.reserve(2);
properties2[U("Email")] = azure::storage::entity_property(U("Ben@contoso.com"));
properties2[U("Phone")] = azure::storage::entity_property(U("425-555-0102"));

// Create a third customer entity to add to the table.
azure::storage::table_entity customer3(U("Smith"), U("Denise"));

azure::storage::table_entity::properties_type& properties3 = customer3.properties();
properties3.reserve(2);
properties3[U("Email")] = azure::storage::entity_property(U("Denise@contoso.com"));
properties3[U("Phone")] = azure::storage::entity_property(U("425-555-0103"));

// Add customer entities to the batch insert operation.
batch_operation.insert_or_replace_entity(customer1);
batch_operation.insert_or_replace_entity(customer2);
batch_operation.insert_or_replace_entity(customer3);

// Execute the batch operation.
std::vector<azure::storage::table_result> results = table.execute_batch(batch_operation);

批处理操作的注意事项如下:

  • 在单次批处理操作中最多可以执行 100 个插入、删除、合并、替换、插入或合并以及插入或替换操作(可以是这些操作的任意组合)。
  • 批处理操作也可以包含检索操作,但前提是检索操作是批处理中仅有的操作。
  • 单次批处理操作中的所有条目都必须具有相同的分区键。
  • 批处理操作的数据负载限制为 4MB。

检索分区中的所有条目

若要查询表以获取分区中的所有实体,请使用 table_query 对象。 以下代码示例指定了一个筛选器,以筛选分区键为“Smith”的实体。 此示例会将查询结果中每个实体的字段输出到控制台。

Note

Azure Cosmos DB 中的 C++ 目前不支持这些方法。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Construct the query operation for all customer entities where PartitionKey="Smith".
azure::storage::table_query query;

query.set_filter_string(azure::storage::table_query::generate_filter_condition(U("PartitionKey"), azure::storage::query_comparison_operator::equal, U("Smith")));

// Execute the query.
azure::storage::table_query_iterator it = table.execute_query(query);

// Print the fields for each customer.
azure::storage::table_query_iterator end_of_results;
for (; it != end_of_results; ++it)
{
    const azure::storage::table_entity::properties_type& properties = it->properties();

    std::wcout << U("PartitionKey: ") << it->partition_key() << U(", RowKey: ") << it->row_key()
        << U(", Property1: ") << properties.at(U("Email")).string_value()
        << U(", Property2: ") << properties.at(U("Phone")).string_value() << std::endl;
}  

此示例中的查询将检索出与筛选条件匹配的所有条目。 如果有大型表并需要经常下载表条目,建议改为将数据存储在 Azure 存储 Blob 中。

检索分区中的一部分条目

如果不想查询分区中的所有条目,则可以通过结合使用分区键筛选器与行键筛选器来指定一个范围。 以下代码示例使用两个筛选器来获取分区“Smith”中的、行键(名字)以字母“E”前面的字母开头的所有条目,并输出查询结果。

Note

Azure Cosmos DB 中的 C++ 目前不支持这些方法。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Create the table query.
azure::storage::table_query query;

query.set_filter_string(azure::storage::table_query::combine_filter_conditions(
    azure::storage::table_query::generate_filter_condition(U("PartitionKey"),
    azure::storage::query_comparison_operator::equal, U("Smith")),
    azure::storage::query_logical_operator::op_and,
    azure::storage::table_query::generate_filter_condition(U("RowKey"), azure::storage::query_comparison_operator::less_than, U("E"))));

// Execute the query.
azure::storage::table_query_iterator it = table.execute_query(query);

// Loop through the results, displaying information about the entity.
azure::storage::table_query_iterator end_of_results;
for (; it != end_of_results; ++it)
{
    const azure::storage::table_entity::properties_type& properties = it->properties();

    std::wcout << U("PartitionKey: ") << it->partition_key() << U(", RowKey: ") << it->row_key()
        << U(", Property1: ") << properties.at(U("Email")).string_value()
        << U(", Property2: ") << properties.at(U("Phone")).string_value() << std::endl;
}  

检索单个实体

可以编写查询以检索单个特定实体。 以下代码使用 table_operation::retrieve_entity 来指定客户“Jeff Smith”。 此方法只返回一个实体,而不是一个集合,并且返回的值在 table_result 中。 在查询中指定分区键和行键是从表服务中检索单个实体的最快方法。

azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Retrieve the entity with partition key of "Smith" and row key of "Jeff".
azure::storage::table_operation retrieve_operation = azure::storage::table_operation::retrieve_entity(U("Smith"), U("Jeff"));
azure::storage::table_result retrieve_result = table.execute(retrieve_operation);

// Output the entity.
azure::storage::table_entity entity = retrieve_result.entity();
const azure::storage::table_entity::properties_type& properties = entity.properties();

std::wcout << U("PartitionKey: ") << entity.partition_key() << U(", RowKey: ") << entity.row_key()
    << U(", Property1: ") << properties.at(U("Email")).string_value()
    << U(", Property2: ") << properties.at(U("Phone")).string_value() << std::endl;

替换条目

要替换条目,请从表服务中检索它,修改条目对象,然后将更改保存回表服务。 以下代码更改现有客户的电话号码和电子邮件地址。 此代码不是调用 table_operation::insert_entity,而是使用 table_operation::replace_entity。 这会导致在服务器上完全替换该实体,除非服务器上的该实体自检索到它以后发生更改,在此情况下,该操作将失败。 操作失败将防止你的应用程序无意中覆盖应用程序的其他组件在检索与更新之间所做的更改。 正确处理此失败的方法是再次检索实体,进行更改(如果仍有效),并执行另一个 table_operation::replace_entity 操作。 下一节将演示如何重写此行为。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Replace an entity.
azure::storage::table_entity entity_to_replace(U("Smith"), U("Jeff"));
azure::storage::table_entity::properties_type& properties_to_replace = entity_to_replace.properties();
properties_to_replace.reserve(2);

// Specify a new phone number.
properties_to_replace[U("Phone")] = azure::storage::entity_property(U("425-555-0106"));

// Specify a new email address.
properties_to_replace[U("Email")] = azure::storage::entity_property(U("JeffS@contoso.com"));

// Create an operation to replace the entity.
azure::storage::table_operation replace_operation = azure::storage::table_operation::replace_entity(entity_to_replace);

// Submit the operation to the Table service.
azure::storage::table_result replace_result = table.execute(replace_operation);

插入或替换实体

如果该实体自从服务器中检索到它以后发生更改,则 table_operation::replace_entity 操作将失败。 此外,必须首先从服务器中检索该实体,table_operation::replace_entity 才会成功。 但是,有时你不知道服务器上是否存在该实体以及存储在其中的当前值是否无关 - 更新操作应将其全部覆盖。 为此,应使用 table_operation::insert_or_replace_entity 操作。 如果该条目不存在,此操作会插入它,如果存在则替换它,而不考虑上次更新时间。 在以下代码示例中,仍将检索 Jeff Smith 的客户实体,但稍后会通过 table_operation::insert_or_replace_entity 将其保存回服务器。 将覆盖在检索与更新操作之间对实体进行的任何更新。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Insert-or-replace an entity.
azure::storage::table_entity entity_to_insert_or_replace(U("Smith"), U("Jeff"));
azure::storage::table_entity::properties_type& properties_to_insert_or_replace = entity_to_insert_or_replace.properties();

properties_to_insert_or_replace.reserve(2);

// Specify a phone number.
properties_to_insert_or_replace[U("Phone")] = azure::storage::entity_property(U("425-555-0107"));

// Specify an email address.
properties_to_insert_or_replace[U("Email")] = azure::storage::entity_property(U("Jeffsm@contoso.com"));

// Create an operation to insert-or-replace the entity.
azure::storage::table_operation insert_or_replace_operation = azure::storage::table_operation::insert_or_replace_entity(entity_to_insert_or_replace);

// Submit the operation to the Table service.
azure::storage::table_result insert_or_replace_result = table.execute(insert_or_replace_operation);

查询条目属性的子集

对表的查询可以只检索实体中的少数几个属性。 以下代码中的查询使用 table_query::set_select_columns 方法,仅返回表中实体的电子邮件地址。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Define the query, and select only the Email property.
azure::storage::table_query query;
std::vector<utility::string_t> columns;

columns.push_back(U("Email"));
query.set_select_columns(columns);

// Execute the query.
azure::storage::table_query_iterator it = table.execute_query(query);

// Display the results.
azure::storage::table_query_iterator end_of_results;
for (; it != end_of_results; ++it)
{
    std::wcout << U("PartitionKey: ") << it->partition_key() << U(", RowKey: ") << it->row_key();

    const azure::storage::table_entity::properties_type& properties = it->properties();
    for (auto prop_it = properties.begin(); prop_it != properties.end(); ++prop_it)
    {
        std::wcout << ", " << prop_it->first << ": " << prop_it->second.str();
    }

    std::wcout << std::endl;
}

Note

查询实体的几个属性是比检索所有属性更高效的操作。

删除条目

可以在检索到实体后轻松将其删除。 检索到实体后,对要删除的实体调用 table_operation::delete_entity。 然后调用 cloud_table.execute 方法。 以下代码检索并删除分区键为“Smith”、行键为“Jeff”的实体。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Create an operation to retrieve the entity with partition key of "Smith" and row key of "Jeff".
azure::storage::table_operation retrieve_operation = azure::storage::table_operation::retrieve_entity(U("Smith"), U("Jeff"));
azure::storage::table_result retrieve_result = table.execute(retrieve_operation);

// Create an operation to delete the entity.
azure::storage::table_operation delete_operation = azure::storage::table_operation::delete_entity(retrieve_result.entity());

// Submit the delete operation to the Table service.
azure::storage::table_result delete_result = table.execute(delete_operation);  

删除表

最后,以下代码示例将从存储帐户中删除表。 在删除表之后的一段时间内无法重新创建它。

// Retrieve the storage account from the connection string.
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(storage_connection_string);

// Create the table client.
azure::storage::cloud_table_client table_client = storage_account.create_cloud_table_client();

// Create a cloud table object for the table.
azure::storage::cloud_table table = table_client.get_table_reference(U("people"));

// Delete the table if it exists
if (table.delete_table_if_exists())
    {
        std::cout << "Table deleted!";
    }
    else
    {
        std::cout << "Table didn't exist";
    }

故障排除

  • Visual Studio 2017 Community Edition 中的生成错误

    如果项目因 include 文件 storage_account.h 和 table.h 而显示生成错误,请删除 /permissive- 编译器开关。

    • 在“解决方案资源管理器”中,右键单击项目,并选择“属性”。
    • 在“属性页”对话框中,依次展开“配置属性”、“C/C++”,然后选择“语言”。
    • 将“符合模式”设置为“否”。

后续步骤

单击以下链接详细了解 Azure Cosmos DB 中的 Azure 存储: