前言
微软官方称为 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);
}
}
}
删除存储表
通过 CloudTable
的 DeleteIfExistsAsync
可以实现对存储表进行删除。在 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 代码仓库