我想告诉您有关Cosmos DB的信息以及如何改进您的代码。当您看到Microsoft.Azure.Cosmos.Table
软件包已弃用时,您可能已经面对了案件。这意味着您需要迁移到Modern Azure.Data.Tables
软件包。我会尽可能地告诉你。
为了澄清这一点,我创建了一个解决方案和两个简单的控制台项目。这两个项目都在实现Cosmos表数据库的CRUD功能的课堂上。在每个步骤中,我都会解释发生了什么或添加的内容。让我们走。
最初的
为方便起见,我在构造函数中添加了创建客户端,我们不需要在我们想要做某事时传递连接字符串。但是,存在一些差异。如您所见,现代实现最短。我们不再需要解析连接字符串。我们可以立即初始一个客户。
private readonly CloudTableClient _client;
public LegacyCosmosTable(string connectionString)
{
var storageAccount = CloudStorageAccount.Parse(connectionString);
_client = storageAccount.CreateCloudTableClient();
}
private readonly TableServiceClient _client;
public ModernCosmosTable(string connectionString)
{
_client = new TableServiceClient(connectionString);
}
在我们开始实现第一个CRUD函数之前,让我们为每个功能创建可重复的调用。
//legacy
private CloudTable GetCloudTable(string tableName)
{
var table = _client.GetTableReference(tableName);
return table;
}
//modern
private TableClient GetTableClient(string tableName)
{
var tableClient = _client.GetTableClient(tableName);
return tableClient;
}
创造
Cosmos DB能够检查表是否已经存在,然后返回表或创建新表。
//legacy
public async Task<CloudTable> CreateTableIfToExistsAsync(string tableName)
{
var table = _client.GetTableReference(tableName);
await table.CreateIfNotExistsAsync().ConfigureAwait(false);
return table;
}
//modern
public async Task<TableClient> CreateTableIfToExistsAsync(string tableName)
{
var table = _client.GetTableClient(tableName);
await table.CreateIfNotExistsAsync().ConfigureAwait(false);
return table;
}
读
让我们实现所有实体。比较最短,最简单的现代实施。我们还使用了ITableEntity
而不是TableEntity
。
//legacy
public async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(string tableName) where TEntity : TableEntity, new()
{
var table = GetCloudTable(tableName);
var tableQuery = new TableQuery<TEntity>();
var results = new List<TEntity>();
TableContinuationToken? token = null;
do
{
var segment = await table.ExecuteQuerySegmentedAsync(tableQuery, token).ConfigureAwait(false);
token = segment.ContinuationToken;
results.AddRange(segment);
} while (token != null);
return results;
}
//modern
public async Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(string tableName)
where TEntity : class, ITableEntity, new()
{
TableClient tableClient = GetTableClient(tableName);
var results = new List<TEntity>();
await foreach (var entity in tableClient.QueryAsync<TEntity>())
{
results.Add(entity);
}
return result;
}
一切都很好,但是如果我想过滤数据?让我们创建下一个功能。您如何看到这两种方法相似,并且具有两个过滤器,通过分区键和我们要选择的列。
//legacy
public async Task<IEnumerable<TEntity>> GetByPartitionKeyAsync<TEntity>(string tableName, string partitionKey,
string[]? columns = null) where TEntity : TableEntity, new()
{
var table = GetCloudTable(tableName);
TableQuery<TEntity> tableQuery;
if (columns != null)
{
tableQuery = new TableQuery<TEntity>()
.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey))
.Select(columns);
}
else
{
tableQuery = new TableQuery<TEntity>()
.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
}
var results = new List<TEntity>();
TableContinuationToken token = null;
do
{
var segment = await table.ExecuteQuerySegmentedAsync(tableQuery, token).ConfigureAwait(false);
token = segment.ContinuationToken;
results.AddRange(segment);
} while (token != null);
return results;
}
//modern
public async Task<IEnumerable<TEntity>> GetByPartitionKeyAsync<TEntity>(string tableName, string partitionKey,
string[]? columns = null) where TEntity : class, ITableEntity, new()
{
TableClient tableClient = GetTableClient(tableName);
var results = new List<TEntity>();
if (columns != null)
{
await foreach (var entity in tableClient.QueryAsync<TEntity>(filter => filter.PartitionKey == partitionKey,
select: columns))
{
results.Add(entity);
}
}
else
{
await foreach (var entity in tableClient.QueryAsync<TEntity>(filter => filter.PartitionKey == partitionKey))
{
results.Add(entity);
}
}
return results;
}
如果您只想获得一个实体,我们需要通过键行,然后查看:
//legacy
public async Task<TEntity> GetAsync<TEntity>(string tableName, string partitionKey, string rowKey)
where TEntity : TableEntity
{
var table = GetCloudTable(tableName);
var get = TableOperation.Retrieve<TEntity>(partitionKey, rowKey);
var result = await table.ExecuteAsync(get).ConfigureAwait(false);
return (TEntity)result.Result;
}
//modern
public async Task<TEntity> GetAsync<TEntity>(string tableName, string partitionKey, string rowKey)
where TEntity : class, ITableEntity
{
TableClient tableClient = GetTableClient(tableName);
var result = await tableClient.GetEntityAsync<TEntity>(partitionKey, rowKey).ConfigureAwait(false);
return result;
}
更新
现在,让我们考虑更新表。与关系数据库不同,数据库之间存在一些差异。在这里,我们可以替换或合并数据。让我们通过合并来实现插入。值得注意的是,默认情况下,现代方法将数据合并最短,最简单。
//legacy
public async Task InsertOrMergeAsync(string tableName, TableEntity entity)
{
var table = GetCloudTable(tableName);
var insert = TableOperation.InsertOrMerge(entity);
await table.ExecuteAsync(insert).ConfigureAwait(false);
}
//modern
public async Task InsertOrMergeAsync(string tableName, ITableEntity entity)
{
TableClient tableClient = GetTableClient(tableName);
await tableClient.UpsertEntityAsync(entity);
}
如果仅需要插入数据,则可以使用以下代码。但是,在现代方法中,您应该通过ETag
属性。您可以阅读link。
//legacy
public async Task InsertAsync(string tableName, TableEntity entity)
{
var table = _client.GetTableReference(tableName);
var insert = TableOperation.Insert(entity);
await table.ExecuteAsync(insert).ConfigureAwait(false);
}
//modern
public async Task InsertAsync(string tableName, ITableEntity entity)
{
TableClient tableClient = GetTableClient(tableName);
await tableClient.UpdateEntityAsync(entity, entity.ETag);
}
如果您想插入数据并且存在此数据,那么该怎么办?它类似于以前的方法。在现代方法中,您只需要添加更换的标志。
//legacy
public async Task InsertOrReplaceAsync(string tableName, TableEntity entity)
{
var table = GetCloudTable(tableName);
var insert = TableOperation.InsertOrReplace(entity);
await table.ExecuteAsync(insert).ConfigureAwait(false);
}
//modern
public async Task InsertOrReplaceAsync(string tableName, ITableEntity entity)
{
TableClient tableClient = GetTableClient(tableName);
await tableClient.UpsertEntityAsync(entity, TableUpdateMode.Replace);
}
删除
现在我们来删除了。让我们考虑批次删除。
//legacy
public async Task<IList<TableResult>> DeleteByPartitionKeyBatchAsync<TEntity>(string tableName,
string partitionKey, string[]? columns = null) where TEntity : TableEntity, new()
{
IList<TableResult> results = new List<TableResult>();
IEnumerable<TEntity> entities =
await GetByPartitionKeyAsync<TEntity>(tableName, partitionKey, columns)
.ConfigureAwait(false);
if (entities.Any())
{
results = await DeleteBatchAsync(tableName, entities).ConfigureAwait(false);
}
return results;
}
//modern
public async Task<Response<IReadOnlyList<Response>>?> DeleteByPartitionKeyBatchAsync<TEntity>(string tableName,
string partitionKey, string[]? columns = null) where TEntity : class, ITableEntity, new()
{
Response<IReadOnlyList<Response>>? results;
IEnumerable<TEntity> entities = await GetByPartitionKeyAsync<TEntity>(tableName, partitionKey, columns)
.ConfigureAwait(false);
if (!entities.Any()) return null;
results = await DeleteBatchAsync(tableName, entities).ConfigureAwait(false);
return results;
}
但是不是全部。我们需要实现删除。为此,您需要以下方法。如您所见,您需要通过可以为null的ETag
。
//legacy
private async Task<IList<TableResult>> DeleteBatchAsync<TEntity>(string tableName, IEnumerable<TEntity> entities)
where TEntity : TableEntity, new()
{
var table = GetCloudTable(tableName);
var batchOperation = new TableBatchOperation();
foreach (var e in entities)
{
e.ETag = e.ETag ?? "*";
batchOperation.Delete(e);
}
return await table.ExecuteBatchAsync(batchOperation).ConfigureAwait(false);
}
//modern
private async Task<Response<IReadOnlyList<Response>>?> DeleteBatchAsync<TEntity>(string tableName,
IEnumerable<TEntity> entities) where TEntity : ITableEntity, new()
{
TableClient tableClient = GetTableClient(tableName);
var batch = new List<TableTransactionAction>();
foreach (var entity in entities)
{
batch.Add(new TableTransactionAction(TableTransactionActionType.Delete, entity));
}
return await tableClient.SubmitTransactionAsync(batch).ConfigureAwait(false);
}
如果您只需要一个实体,那就更容易了。
//legacy
public async Task DeleteEntityAsync(string tableName, TableEntity entity)
{
var table = GetCloudTable(tableName);
entity.ETag ??= "*";
var delete = TableOperation.Delete(entity);
await table.ExecuteAsync(delete).ConfigureAwait(false);
}
//modern
public async Task DeleteEntityAsync(string tableName, ITableEntity entity)
{
TableClient tableClient = GetTableClient(tableName);
await tableClient.DeleteEntityAsync(entity.PartitionKey, entity.RowKey, entity.ETag).ConfigureAwait(false);
}
现在让我们清除桌子。 Cosmos DB没有一种特殊的方法来清除表中的数据,例如SQL中的截断。但是,我们有可以做到的疑问。两种方法都是相等的。
//legacy and modern
public async Task ClearTableAsync(string tableName)
{
var entities = await GetAllAsync<TableEntity>(tableName).ConfigureAwait(false);
if (entities.Any())
{
await DeleteBatchAsync(tableName, entities).ConfigureAwait(false);
}
}
确保您可能想删除表:
//legacy
public async Task DeleteTableAsync(string tableName)
{
var table = GetCloudTable(tableName);
await table.DeleteAsync().ConfigureAwait(false);
}
//modern
public async Task DeleteTableAsync(string tableName)
{
var table = GetTableClient(tableName);
await table.DeleteAsync().ConfigureAwait(false);
}
结论
通过实现,新的API变得更加可读性和最小。就遗留图书馆的弃用而言,我强烈建议您利用本文迁移到最新的图书馆。
感谢您阅读,像订阅一样,并在下一篇文章中与您见面。愉快的编码!
您可以通过link的源代码。