.Net 中的 Azure Storage Table 的应用

前言

  微软官方称为 Azure Cosmos DB 表,是一种 NoSQL 的数据存储服务。

微软文档:
  Azure 表存储可存储大量结构化数据。 该服务是一个 NoSQL 数据存储,接受来自 Azure 云内部和外部的通过验证的呼叫。 Azure 表最适合存储结构化非关系型数据。 表存储的常见用途包括:

  • 存储 TB 量级的结构化数据,能够为 Web 规模应用程序提供服务
  • 存储无需复杂联接、外键或存储过程,并且可以对其进行非规范化以实现快速访问的数据集
  • 使用聚集索引快速查询数据
  • 使用 OData 协议和 LINQ 查询以及 WCF 数据服务 .NET 库访问数据

  可以使用表存储来存储和查询大型结构化非关系型数据集,并且表会随着需求的增加而扩展。

  当打开 Microsoft Azure Storage Explorer 时,如果你加入了某个组织或者订阅,你会在资源管理器中看到如下结构的资源集:


    ——资源管理器
      |_  您的订阅集
        |_  存储账户
          |_  存储账户集
            |_  Blob Containers
            |_  File Shares
            |_  Queues
            |_  Tables


  而接下来要说的就是 Tables ,用Net实现对 Azure Cosmos DB 表实现 CRUD 操作。

创建项目

  创建一个 .Net Core 的控制台应用程序 AzureStorageConsoleDemo,安装以下 NuGet 包。
      Microsoft.Azure.Cosmos.Table
      Microsoft.Extensions.Configuration
      Microsoft.Extensions.Configuration.Json
      Microsoft.Extensions.Configuration.Binder
  在项目中创建如下文件夹、类文件以及配置文件。
创建项目

AzureTableDemo 文件夹: 存放关于 StorageTable 方面的一些代码
StorageTableHelp.cs 文件: 关于 Table 的一些操作方面的代码
WorkerModel.cs 文件: 演示使用的实体模型
Settings.json 配置文件: 存储演示所需要的一些配置项,如 Azure Storage 的链接字符串

创建模型

  在 AzureTableDemo/WorkerModel.cs 中键入如下代码,这就是此演示所需要用到的数据模型。

using Microsoft.Azure.Cosmos.Table;

namespace AzureStorageConsoleDemo.AzureTableDemo
{
    class WorkerModel : TableEntity
    {
        public WorkerModel()
        {
        }

        public WorkerModel(string lastName, string firstName)
        {
            PartitionKey = lastName;
            RowKey = firstName;
        }
        /// <summary>
        /// 职工姓名
        /// </summary>
        public string WorkerName { get; set; }
        /// <summary>
        /// 工号
        /// </summary>
        public string JobNum { get; set; }
        /// <summary>
        /// 部门
        /// </summary>
        public string Department { get; set; }
    }
}

设置配置文件

  因为链接字符串是通常都需要用到的,所以这里将存储库账户的连接字符串放入配置文件中。如下代码所示。

{
  "StorageConnectionString": "《Azure Storage 存储账户连接字符串》"
}

StorageTableHelp 获取链接字符串

  在 StorageTableHelp.cs 中键入如下代码,此做法是或许配置文件中的连接字符串并缓存起来。

using Microsoft.Azure.Cosmos.Table;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace AzureStorageConsoleDemo.AzureTableDemo
{
    class StorageTableHelp
    {
        /// <summary>
        /// Storage 连接字符串
        /// </summary>
        public string StorageConnectionString { get; set; }

        /// <summary>
        /// 初始化程序配置文件
        /// </summary>
        /// <returns>StorageTableHelp</returns>
        public static StorageTableHelp LoadAppSettings()
        {
            IConfigurationRoot configRoot = new ConfigurationBuilder().AddJsonFile("Settings.json").Build();
            StorageTableHelp appSettings = configRoot.Get<StorageTableHelp>();
            return appSettings;
        }
    }
}

检验链接字符串

  这里需要进行分析连接字符串详细信息,并验证提供的帐户名称和帐户密钥详细信息是否有效。在 StorageTableHelp.cs 中的 LoadAppSettings() 方法下方键入如下方法。

/// <summary>
/// 通过链接字符串创建存储账户,并检测其是否为有效链接
/// </summary>
/// <param name="storageConnectionString">Storage 连接字符串</param>
/// <returns>CloudStorageAccount</returns>
public static CloudStorageAccount CreateStorageAccountFromConnectionString(string storageConnectionString)
{
    CloudStorageAccount storageAccount;
    try
    {
        storageAccount = CloudStorageAccount.Parse(storageConnectionString);
    }
    catch (FormatException)
    {
        throw new FormatException("提供的存储帐户信息无效。 请确认 app.config 文件中的 AccountName 和 AccountKey 有效 - 然后重新启动应用程序。");
    }
    catch (ArgumentException)
    {
        throw new ArgumentException("提供的存储帐户信息无效。 请确认 app.config 文件中的 AccountName 和 AccountKey 有效 - 然后重新启动示例。");
    }
    return storageAccount;
}

存储表操作

创建存储表

  当每一次对表数据进行操作之前,需要对数据表进行检验其是否存在。这里的创建数据表可以检测数据表是否存在,如果存在则返回 CloudTable ,否则创建数据表后再返回 CloudTable 。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 异步创建存储表,不存在则创建,存在则不创建
/// </summary>
/// <param name="tableName">存储表名称</param>
/// <returns>CloudTable</returns>
public static async Task<CloudTable> CreateTableAsync(string tableName)
{
    string storageConnectionString = LoadAppSettings().StorageConnectionString;
    // 从连接字符串中检索存储帐户信息。
    CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString(storageConnectionString);
    // 创建用于与表服务交互的表客户端
    CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());
    // 创建用于与表服务交互的表客户端
    CloudTable table = tableClient.GetTableReference(tableName);
    // 创建表
    await table.CreateIfNotExistsAsync();
    return table;
}

  方法使用示例:Program.cs 文件中键入如下代码。

using AzureStorageConsoleDemo.AzureTableDemo;
using Microsoft.Azure.Cosmos.Table;
using System;
using System.Data;
using System.Threading.Tasks;

namespace AzureStorageConsoleDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
        	// 创建数据表名称
            string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
            // 创建或引用现有表,返回的 CloudTable 可用于表操作
            CloudTable table =await StorageTableHelp.CreateTableAsync(tableName);
        }
    }
}

删除存储表

  通过 CloudTableDeleteIfExistsAsync 可以实现对存储表进行删除。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 删除存储表
/// </summary>
/// <param name="table">CloudTable</param>
/// <returns></returns>
public static async Task DeleteTableAsync(CloudTable table)
{
    await table.DeleteIfExistsAsync();
}

  方法使用示例:

static async Task Main(string[] args)
{
    // 创建数据表名称
    string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
    // 创建或引用现有表,返回的 CloudTable 可用于表操作
    CloudTable table =await StorageTableHelp.CreateTableAsync(tableName);
	// 删除表
    await StorageTableHelp.DeleteTableAsync(table);
}

插入/修改一条数据

  采用 TableOperation.InsertOrMerge(entity) 方法可以对数据进行插入,如果当前实体所对应的分区键值( PartitionKey )1和行键值( RowKey )2相同,那么就是对这个实体数据进行更改。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 插入数据,如果分区与行键值相同则修改该条数据
/// </summary>
/// <typeparam name="T">实体模型</typeparam>
/// <param name="table">CloudTable</param>
/// <param name="entity">数据对象</param>
/// <returns></returns>
public static async Task<T> InsertOrMergeEntityAsync<T>(CloudTable table, T entity) where T : TableEntity
{
    if (entity == null)
    {
        throw new ArgumentNullException("实体不允许为 null");
    }
    try
    {
        // 创建 InsertOrReplace 表操作
        TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);
        // 执行操作。
        TableResult result = await table.ExecuteAsync(insertOrMergeOperation);
        return result.Result as T;
    }
    catch (StorageException e)
    {
        throw new StorageException(e.Message);
    }
}

  方法使用示例:

static async Task Main(string[] args)
{
    // 创建数据表名称
    string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
    // 创建或引用现有表,返回的 CloudTable 可用于表操作
    CloudTable table =await StorageTableHelp.CreateTableAsync(tableName);
	// 创建职工实体的实例。
	WorkerModel workerModel = new WorkerModel("Worker", Guid.NewGuid().ToString())
	{
	    WorkerName = "张三",
	    JobNum = "20210728",
	    Department = "外交部"
	};
	// 向存储表中插入上面这个实体数据
	WorkerModel workerModel1 = await StorageTableHelp.InsertOrMergeEntityAsync(table, workerModel);
	Console.WriteLine("\n{0}\n{1}\n{2}\n{3}\n{4}", workerModel1.PartitionKey, workerModel1.RowKey, workerModel1.WorkerName, workerModel1.JobNum, workerModel1.Department);
	workerModel1.Department = "策划部";
	// 上面插入的这条数据的部门字段在下面指定执行中已经被修改
	WorkerModel workerModel2 = await StorageTableHelp.InsertOrMergeEntityAsync(table, workerModel1);
	Console.WriteLine("\n{0}\n{1}\n{2}\n{3}\n{4}", workerModel2.PartitionKey, workerModel2.RowKey, workerModel2.WorkerName, workerModel2.JobNum, workerModel2.Department);
}

通过 PartitionKey 与 RowKey 检索一条数据

  当一个 Table 中的 PartitionKey 和 RowKey 相同时表示同一条数据/实体,所以,这里可以通过 PartitionKey 与 RowKey 来检索指定的一条数据。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 通过 partitionKey 与 rowKey 检索一条数据
/// </summary>
/// <typeparam name="T">实体模型</typeparam>
/// <param name="table">CloudTable</param>
/// <param name="partitionKey">partitionKey</param>
/// <param name="rowKey">rowKey</param>
/// <returns></returns>
public static async Task<T> RetrieveEntityUsingPointQueryAsync<T>(CloudTable table, string partitionKey, string rowKey) where T : TableEntity
{
    try
    {
        TableOperation retrieveOperation = TableOperation.Retrieve<T>(partitionKey, rowKey);
        TableResult result = await table.ExecuteAsync(retrieveOperation);
        if (result.Result is T customer)
        {
            return result.Result as T;
        }
        return null;
    }
    catch (StorageException e)
    {
        throw new StorageException(e.Message);
    }
}

  方法使用示例:

// 检索一条数据
WorkerModel workerModel = await StorageTableHelp.RetrieveEntityUsingPointQueryAsync<WorkerModel>(table, "PartitionKey", "RowKey");

删除一条数据

  在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 删除一条数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="table">CloudTable</param>
/// <param name="deleteEntity">数据对象</param>
/// <returns></returns>
public static async Task DeleteEntityAsync<T>(CloudTable table, T deleteEntity) where T : TableEntity
{
    try
    {
        if (deleteEntity == null)
        {
            throw new ArgumentNullException("实体不存在");
        }
        TableOperation deleteOperation = TableOperation.Delete(deleteEntity);
        TableResult result = await table.ExecuteAsync(deleteOperation);
    }
    catch (StorageException e)
    {
        throw new StorageException(e.Message);
    }
}

  方法使用示例:

static async Task Main(string[] args)
{
    // 创建数据表名称
    string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
    // 创建或引用现有表,返回的 CloudTable 可用于表操作
    CloudTable table =await StorageTableHelp.CreateTableAsync(tableName);
	// 创建职工实体的实例。
	WorkerModel workerModel = new WorkerModel("Worker", Guid.NewGuid().ToString())
	{
	    WorkerName = "张三",
	    JobNum = "20210728",
	    Department = "外交部"
	};
	// 向存储表中插入上面这个实体数据
	workerModel = await StorageTableHelp.InsertOrMergeEntityAsync(table, workerModel);
	// 上面插入的这条数据在下面指定执行中已经被删除
	await StorageTableHelp.DeleteEntityAsync(table, workerModel);
}

列出账户中所有的数据表

  可以使用 CloudTableClient 类中的方法 ListTables() 来获取账户中存在的所有存储表,也可以通过 ListTablesSegmented() 方法来获取名称由某个字符串开头的存储表。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 列出 CloudTableClient 下所有的数据表
/// </summary>
/// <param name="tableClient">CloudTableClient</param>
/// <returns></returns>
public static IEnumerable<CloudTable> ListTables(CloudTableClient tableClient)
{
    return tableClient.ListTables();
}

/// <summary>
/// 列出 CloudTableClient 下所有名称以 prefix 开头的数据表
/// </summary>
/// <param name="tableClient">CloudTableClient</param>
/// <param name="prefix"></param>
/// <returns></returns>
public static IEnumerable<CloudTable> ListTables(CloudTableClient tableClient, string prefix)
{
    TableContinuationToken continuationToken = null;
    TableResultSegment resultSegment = tableClient.ListTablesSegmented(prefix, continuationToken);
    return resultSegment.Results;
}

  方法使用示例:

static async Task Main(string[] args)
{
    string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
    // 创建或引用现有表
    CloudTable table =await StorageTableHelp.CreateTableAsync(tableName);
    // 获取当前 CloudTable 所在的账户客户端
    CloudTableClient cloudTableClient = table.ServiceClient;
    Console.WriteLine("所有数据表:");
    foreach (var item in StorageTableHelp.ListTables(cloudTableClient))
    {
        Console.WriteLine("\t表:" + item.Name);
    }
    Console.WriteLine("demo 开头的数据表:");
    foreach (var item in StorageTableHelp.ListTables(cloudTableClient, "demo"))
    {
        Console.WriteLine("\t表:" + item.Name);
    }
}

批量插入数据

  如果需要将一个 List 的实体集添加到 Table 中,那么可以使用批处理插入数据。在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 批量插入数据
/// </summary>
/// <typeparam name="T">实体模型</typeparam>
/// <param name="table">CloudTable</param>
/// <param name="entityList">数据集</param>
/// <returns></returns>
public static async Task<TableBatchResult> BatchInsertEntitiesAsync<T>(CloudTable table, List<T> entityList) where T : TableEntity
{
    try
    {
        // 创建批处理操作。
        TableBatchOperation batchOperation = new TableBatchOperation();
        // 将需要插入的数据加入到批处理操作中
        entityList.ForEach(entity =>
        {
            batchOperation.Insert(entity);
        });
        // 执行批处理操作。
        TableBatchResult results = await table.ExecuteBatchAsync(batchOperation);
        return results;
    }
    catch (StorageException e)
    {
        throw new StorageException(e.Message);
    }
}

  方法使用示例:

static async Task Main(string[] args)
{
    string tableName = "demo" + Guid.NewGuid().ToString().Substring(0, 5);
    // 创建或引用现有表
    CloudTable table = await StorageTableHelp.CreateTableAsync(tableName);
    // 申明实体数据集
    List<WorkerModel> workerModels = new List<WorkerModel>();
    // 创建演示数据,不允许有相同分区和行键值的数据
    for (int i = 0; i < 20; i++)
    {
        workerModels.Add(new WorkerModel("Harp", Guid.NewGuid().ToString())
        {
            WorkerName = $"{i}王二",
            JobNum = $"{i}20210728",
            Department = $"{i}策划部"
        });
    }
    // 调用批量插入数据方法
    var results= await StorageTableHelp.BatchInsertEntitiesAsync(table, workerModels);
    // 遍历打印出刚刚批量插入的数据
    foreach (var res in results)
    {
        var workerModel = res.Result as WorkerModel;
        Console.WriteLine("\t{0}\t{1}\t{2}\t{3}\t{4}", workerModel.PartitionKey, workerModel.RowKey, workerModel.WorkerName, workerModel.JobNum, workerModel.Department, workerModel.Timestamp);
    }
}

查询某个分区下的所有数据

  在 StorageTableHelp.cs 中键入如下代码。

/// <summary>
/// 查询一个分区中的所有数据
/// </summary>
/// <param name="table">CloudTable</param>
/// <param name="partitionKey">分区名称</param>
/// <returns></returns>
public static async Task<List<WorkerModel>> PartitionScanAsync(CloudTable table, string partitionKey)
{
    TableQuery<WorkerModel> partitionScanQuery =
            new TableQuery<WorkerModel>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
    TableContinuationToken token = null;
    TableQuerySegment<WorkerModel> segment = await table.ExecuteQuerySegmentedAsync(partitionScanQuery, token);
    List<WorkerModel> entitys = new List<WorkerModel>();
    entitys.AddRange(segment);
    return entitys;
}

  方法使用示例:

static async Task Main(string[] args)
{
    string tableName = "数据表名称";
    // 创建或引用现有表
    CloudTable table = await StorageTableHelp.CreateTableAsync(tableName);
    List<WorkerModel> workerModels= await StorageTableHelp.PartitionScanAsync(table, "分区名称");
    foreach (WorkerModel workerModel in workerModels)
    {
        Console.WriteLine("\t{0}\t{1}\t{2}\t{3}\t{4}", workerModel.PartitionKey, workerModel.RowKey, workerModel.WorkerName, workerModel.JobNum, workerModel.Department, workerModel.Timestamp);
    }
}

The Ended

  这些都是我根据官方 Demo 经过反复揣摩后,整理出来的。应用还有很多,实在不能逐一枚举,如果你对此感兴趣,可以关注官网教程及 Demo 。

官方教程: https://docs.microsoft.com/zh-cn/azure/storage/tables/
官方 Demo: Github 代码仓库
此博客 Demo: Github 代码仓库


  1. PartitionKey: 分区键。帐户名称、表名称和 PartitionKey 共同标识存储服务中表服务用于存储实体的分区。 ↩︎

  2. RowKey: 行键。类似于一个数据表分区中的一个行索引,要求具有唯一性。 ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值