你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
重要
若要了解 Azure Cosmos DB .NET SDK v3,请参阅 发行说明、 .NET GitHub 存储库、.NET SDK v3 性能提示和 故障排除指南。
本文重点介绍了将现有 .NET 应用程序升级到较新的 Azure Cosmos DB .NET SDK v3 for API for NoSQL 的一些注意事项。 Azure Cosmos DB .NET SDK v3 对应于 Microsoft.Azure.Azure Cosmos DB 命名空间。 如果要从以下任一 Azure Cosmos DB .NET SDK 迁移应用程序,则可以使用此文档中提供的信息:
- Azure Cosmos DB .NET Framework SDK v2 用于 NoSQL 的 API
- Azure Cosmos DB .NET Core SDK v2 for API for NoSQL
本文中的说明还有助于迁移以下外部库,这些库现在是 Azure Cosmos DB .NET SDK v3 for API for NoSQL 的一部分:
- .NET 变更馈送处理器库 2.0
- .NET 批量执行程序库 1.1 或更高版本
.NET V3 SDK 中的新增功能
v3 SDK 包含许多可用性和性能改进,包括:
- 直观的编程模型命名
- .NET Standard 2.0 **
- 通过支持流 API 提高性能
- 流畅的层次结构,取代了对 URI 创建工厂的需求
- 对更改源处理器库的内置支持
- 内置的批量操作支持
- 用于简化单元测试的可模拟 API
- 事务批处理和 Blazor 支持
- 插件式序列化程序
- 扩大非分区容器和自动扩展容器
** SDK 面向 .NET Standard 2.0,将现有的 Azure Cosmos DB .NET Framework 和 .NET Core SDK 统一到单个 .NET SDK 中。 可以在实现 .NET Standard 2.0 的任何平台中使用 .NET SDK,包括 .NET Framework 4.6.1+ 和 .NET Core 2.0+ 应用程序。
大多数网络、重试逻辑和较低级别的 SDK 基本保持不变。
Azure Cosmos DB .NET SDK v3 现在是开放源代码。 欢迎任何拉取请求,并将记录问题并在 GitHub 上跟踪反馈。我们将致力于处理任何可改善客户体验的功能。
为何迁移到 .NET v3 SDK
除了大量可用性和性能改进之外,在最新 SDK 中所做的新功能投资不会移植到旧版本。 v2 SDK 当前处于维护模式。 为了获得最佳开发体验,建议始终从最新支持的 SDK 版本开始。
主要名称从 v2 SDK 更改为 v3 SDK
在整个 .NET 3.0 SDK 中应用了以下名称更改,以符合适用于 NoSQL 的 API 的 API 命名约定:
-
DocumentClient已重命名为CosmosClient -
Collection已重命名为Container -
Document已重命名为Item
所有资源对象都重命名为其他属性,包括资源名称,以便清楚起见。
下面是一些主要类名更改:
| .NET v2 SDK | .NET v3 SDK |
|---|---|
Microsoft.Azure.Documents.Client.DocumentClient |
Microsoft.Azure.Cosmos.CosmosClient |
Microsoft.Azure.Documents.Client.ConnectionPolicy |
Microsoft.Azure.Cosmos.CosmosClientOptions |
Microsoft.Azure.Documents.Client.DocumentClientException |
Microsoft.Azure.Cosmos.CosmosException |
Microsoft.Azure.Documents.Client.Database |
Microsoft.Azure.Cosmos.DatabaseProperties |
Microsoft.Azure.Documents.Client.DocumentCollection |
Microsoft.Azure.Cosmos.ContainerProperties |
Microsoft.Azure.Documents.Client.RequestOptions |
Microsoft.Azure.Cosmos.ItemRequestOptions |
Microsoft.Azure.Documents.Client.FeedOptions |
Microsoft.Azure.Cosmos.QueryRequestOptions |
Microsoft.Azure.Documents.Client.StoredProcedure |
Microsoft.Azure.Cosmos.StoredProcedureProperties |
Microsoft.Azure.Documents.Client.Trigger |
Microsoft.Azure.Cosmos.TriggerProperties |
Microsoft.Azure.Documents.SqlQuerySpec |
Microsoft.Azure.Cosmos.QueryDefinition |
.NET v3 SDK 上替换的类
3.0 SDK 上已替换以下类:
Microsoft.Azure.Documents.UriFactory
Microsoft.Azure.Documents.UriFactory 类已被替换为 Fluent Design 设计语言。
- .NET SDK v3
- .NET SDK v2
Container container = client.GetContainer(databaseName,containerName);
ItemResponse<SalesOrder> response = await this._container.CreateItemAsync(
salesOrder,
new PartitionKey(salesOrder.AccountNumber));
Microsoft.Azure.Documents.Document
由于 .NET v3 SDK 允许用户配置 自定义序列化引擎,因此无法直接替换该 Document 类型。 使用 Newtonsoft.Json(默认序列化引擎)时, JObject 可用于实现相同的功能。 使用不同的序列化引擎时,可以使用其基 json 文档类型(例如 JsonDocument System.Text.Json)。 建议使用 C# 类型来反映项的架构,而不是依赖泛型类型。
Microsoft.Azure.Documents.Resource
Resource 没有直接替代选项,在其用于文档的情况下,请遵循 Document 的指导。
Microsoft.Azure.Documents.AccessCondition
IfNoneMatch 或者 IfMatch 现在可直接在 Microsoft.Azure.Cosmos.ItemRequestOptions 上使用。
对项 ID 生成的更改
.NET v3 SDK 中不再自动填入项 ID。 因此,项目 ID 必须明确包含一个生成的 ID。 查看以下示例:
[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }
更改了连接模式的默认行为
与以前的 v2 SDK 相比,SDK v3 现在默认为 Direct + TCP 连接模式 ,后者默认为网关 + HTTPS 连接模式。 此更改提供增强的性能和可伸缩性。
对 FeedOptions 的更改(v3.0 SDK 中的 QueryRequestOptions)
FeedOptions类在SDK v2中现已在SDK v3中重命名为QueryRequestOptions,并且类中的多个属性的名称和/或默认值都发生了变化,或者完全被删除。
| .NET v2 SDK | .NET v3 SDK |
|---|---|
FeedOptions.MaxDegreeOfParallelism |
QueryRequestOptions.MaxConcurrency - 默认值和关联的行为保持不变,在并行查询执行期间运行客户端的作将以无并行度串行方式执行。 |
FeedOptions.PartitionKey |
QueryRequestOptions.PartitionKey - 行为保持不变。 |
FeedOptions.EnableCrossPartitionQuery |
删除。 SDK 3.0 中的默认行为是,将执行跨分区查询,而无需专门启用属性。 |
FeedOptions.PopulateQueryMetrics |
删除。 它现在默认启用,并且是 诊断的一部分。 |
FeedOptions.RequestContinuation |
删除。 查询方法本身现在被升格为更重要的地位。 |
FeedOptions.JsonSerializerSettings |
删除。 了解如何 自定义序列化 以获取其他信息。 |
FeedOptions.PartitionKeyRangeId |
删除。 可以通过将 FeedRange 作为查询方法的输入来获得相同的结果。 |
FeedOptions.DisableRUPerMinuteUsage |
删除。 |
构造客户端
.NET SDK v3 提供了一个 Fluent CosmosClientBuilder 类,用于替换 SDK v2 URI 工厂的需求。
Fluent 设计在内部生成 URL,并允许只传递一个Container对象,而不是分别传递DocumentClient、DatabaseName和DocumentCollection对象。
以下示例创建了一个具有强 ConsistencyLevel 和首选位置列表的新 CosmosClientBuilder。
CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder(
accountEndpoint: "https://testcosmos.documents.azure.com:443/",
authKeyOrResourceToken: "SuperSecretKey")
.WithConsistencyLevel(ConsistencyLevel.Strong)
.WithApplicationRegion(Regions.EastUS);
CosmosClient client = cosmosClientBuilder.Build();
例外
v2 SDK 使用DocumentClientException来在操作期间发出错误信号,而v3 SDK 使用CosmosException,它公开了StatusCode、Diagnostics以及其他与响应相关的信息。 当使用 ToString() 时,将序列化所有完整信息。
catch (CosmosException ex)
{
HttpStatusCode statusCode = ex.StatusCode;
CosmosDiagnostics diagnostics = ex.Diagnostics;
// store diagnostics optionally with diagnostics.ToString();
// or log the entire error details with ex.ToString();
}
Diagnostics
在 v2 SDK 中,诊断信息仅通过 RequestDiagnosticsString 属性以 Direct 方式提供,而在 v3 SDK 中,Diagnostics 可用于所有响应和异常,且这些信息更丰富,不局限于 Direct 模式。 它们不仅包括在 SDK 上进行操作所花费的时间,还包括操作涉及的区域:
try
{
ItemResponse<MyItem> response = await container.ReadItemAsync<MyItem>(
partitionKey: new PartitionKey("MyPartitionKey"),
id: "MyId");
TimeSpan elapsedTime = response.Diagnostics.GetElapsedTime();
if (elapsedTime > somePreDefinedThreshold)
{
// log response.Diagnostics.ToString();
IReadOnlyList<(string region, Uri uri)> regions = response.Diagnostics.GetContactedRegions();
}
}
catch (CosmosException cosmosException) {
string diagnostics = cosmosException.Diagnostics.ToString();
TimeSpan elapsedTime = cosmosException.Diagnostics.GetElapsedTime();
IReadOnlyList<(string region, Uri uri)> regions = cosmosException.Diagnostics.GetContactedRegions();
// log cosmosException.ToString()
}
连接策略
中的 ConnectionPolicy 某些设置已重命名或替换为 CosmosClientOptions:
| .NET v2 SDK | .NET v3 SDK |
|---|---|
EnableEndpointDiscovery |
LimitToEndpoint- 现在值被反转,如果EnableEndpointDiscovery被设置为true,那么LimitToEndpoint应设置为false。 在使用此设置之前,需要了解 它如何影响客户端。 |
ConnectionProtocol |
删除。 协议与模式相关联,可以是网关(HTTPS)或直接(TCP)。 V3 SDK 不再支持使用 HTTPS 协议的直接模式,建议使用 TCP 协议。 |
MediaRequestTimeout |
删除。 不再支持附件。 |
SetCurrentLocation |
CosmosClientOptions.ApplicationRegion 可用于实现相同的效果。 |
PreferredLocations |
CosmosClientOptions.ApplicationPreferredRegions 可用于实现相同的效果。 |
UserAgentSuffix |
CosmosClientBuilder.ApplicationName 可用于实现相同的效果。 |
UseMultipleWriteLocations |
删除。 SDK 会自动检测帐户是否支持多个写入终结点。 |
索引编制策略
在索引策略中,无法配置这些属性。 如果未指定,这些属性现在将始终具有以下值:
| 属性名称 | 新值 (不可配置) |
|---|---|
Kind |
range |
dataType |
String 和 Number |
有关包含和排除路径的索引策略示例,请参阅此部分。 由于查询引擎的改进,即使使用旧版 SDK,配置这些属性也不会影响性能。
会话令牌
如果 v2 SDK 在需要捕获会话令牌时,将会话令牌作为 ResourceResponse.SessionToken 公开,由于会话令牌是一个标头,v3 SDK 将该值显示在任何响应的 Headers.Session 属性中。
时间戳
如果 v2 SDK 通过 Timestamp 属性公开文档的时间戳,因为 Document 该时间戳不再可用,则用户可以将 _ts系统属性 映射到其模型中的属性。
OpenAsync
对于用于预热 v2 SDK 客户端的用例 OpenAsync() , CreateAndInitializeAsync 可用于 创建和预热 v3 SDK 客户端。
直接从 v3 SDK 使用更改源处理器 API
v3 SDK 具有对更改源处理器 API 的内置支持,允许你使用相同的 SDK 来生成应用程序和更改源处理器实现。 以前,必须使用单独的更改源处理器库。
有关详细信息,请参阅 如何从更改提要处理器库迁移到 Azure Cosmos DB .NET v3 SDK
更改源查询
在 v3 SDK 上执行更改源查询被视为使用 更改源拉取模型。 按照此表迁移配置:
| .NET v2 SDK | .NET v3 SDK |
|---|---|
ChangeFeedOptions.PartitionKeyRangeId |
FeedRange - 为了实现并行读取更改源,可以使用 FeedRanges。 它不再是必需的参数,现在可以轻松 读取针对整个容器的更改源。 |
ChangeFeedOptions.PartitionKey |
FeedRange.FromPartitionKey - 表示所需分区键的 FeedRange 可用于 读取该分区键值的更改馈送。 |
ChangeFeedOptions.RequestContinuation |
ChangeFeedStartFrom.Continuation - 随时可以通过 保存延续并在创建新的迭代器时使用它来停止和恢复更改源迭代器。 |
ChangeFeedOptions.StartTime |
ChangeFeedStartFrom.Time |
ChangeFeedOptions.StartFromBeginning |
ChangeFeedStartFrom.Beginning |
ChangeFeedOptions.MaxItemCount |
ChangeFeedRequestOptions.PageSizeHint - 随时可以通过 保存延续并在创建新的迭代器时使用它来停止和恢复更改源迭代器。 |
IDocumentQuery.HasMoreResults |
response.StatusCode == HttpStatusCode.NotModified - 更改源在概念上是无限的,因此始终可能会有更多结果。 当响应包含 HttpStatusCode.NotModified 状态代码时,这意味着目前没有要读取的新更改。 可以使用它停止并 保存延续 或暂时睡眠或等待,然后再次调用 ReadNextAsync 以测试新更改。 |
| 拆分处理 | 读取更改源时,用户无需处理拆分异常,拆分将以透明的方式自动处理,无需用户交互。 |
直接从 V3 SDK 使用批量执行程序库
v3 SDK 具有对批量执行程序库的内置支持,允许你使用相同的 SDK 来生成应用程序和执行批量作。 以前,需要使用单独的批量执行程序库。
有关详细信息,请参阅 如何从批量执行程序库迁移到 Azure Cosmos DB .NET V3 SDK 中的批量支持
自定义序列化
.NET V2 SDK 允许在操作级别的 RequestOptions 中设置 JsonSerializerSettings,用于反序列化结果文档。
// .NET V2 SDK
var result = await container.ReplaceDocumentAsync(document, new RequestOptions { JsonSerializerSettings = customSerializerSettings })
.NET SDK v3 提供了一个 序列化程序接口 ,用于在客户端构造过程中完全自定义序列化引擎或更多泛型 序列化选项 。
在操作级别自定义序列化可以通过使用 Stream API 来实现:
// .NET V3 SDK
using(Response response = await this.container.ReplaceItemStreamAsync(stream, "itemId", new PartitionKey("itemPartitionKey"))
{
using(Stream stream = response.ContentStream)
{
using (StreamReader streamReader = new StreamReader(stream))
{
// Read the stream and do dynamic deserialization based on type with a custom Serializer
}
}
}
代码片段比较
以下代码片段显示了 .NET v2 和 v3 SDK 之间资源创建方式的差异:
数据库操作
创建数据库
- .NET SDK v3
- .NET SDK v2
// Create database with no shared provisioned throughput
DatabaseResponse databaseResponse = await client.CreateDatabaseIfNotExistsAsync(DatabaseName);
Database database = databaseResponse;
DatabaseProperties databaseProperties = databaseResponse;
// Create a database with a shared manual provisioned throughput
string databaseIdManual = new string(DatabaseName + "_SharedManualThroughput");
database = await client.CreateDatabaseIfNotExistsAsync(databaseIdManual, ThroughputProperties.CreateManualThroughput(400));
// Create a database with shared autoscale provisioned throughput
string databaseIdAutoscale = new string(DatabaseName + "_SharedAutoscaleThroughput");
database = await client.CreateDatabaseIfNotExistsAsync(databaseIdAutoscale, ThroughputProperties.CreateAutoscaleThroughput(4000));
按 ID 读取数据库
- .NET SDK v3
- .NET SDK v2
// Read a database
Console.WriteLine($"{Environment.NewLine} Read database resource: {DatabaseName}");
database = client.GetDatabase(DatabaseName);
Console.WriteLine($"{Environment.NewLine} database { database.Id.ToString()}");
// Read all databases
string findQueryText = "SELECT * FROM c";
using (FeedIterator<DatabaseProperties> feedIterator = client.GetDatabaseQueryIterator<DatabaseProperties>(findQueryText))
{
while (feedIterator.HasMoreResults)
{
FeedResponse<DatabaseProperties> databaseResponses = await feedIterator.ReadNextAsync();
foreach (DatabaseProperties _database in databaseResponses)
{
Console.WriteLine($"{ Environment.NewLine} database {_database.Id.ToString()}");
}
}
}
删除数据库
- .NET SDK v3
- .NET SDK v2
// Delete a database
await client.GetDatabase(DatabaseName).DeleteAsync();
Console.WriteLine($"{ Environment.NewLine} database {DatabaseName} deleted.");
// Delete all databases in an account
string deleteQueryText = "SELECT * FROM c";
using (FeedIterator<DatabaseProperties> feedIterator = client.GetDatabaseQueryIterator<DatabaseProperties>(deleteQueryText))
{
while (feedIterator.HasMoreResults)
{
FeedResponse<DatabaseProperties> databaseResponses = await feedIterator.ReadNextAsync();
foreach (DatabaseProperties _database in databaseResponses)
{
await client.GetDatabase(_database.Id).DeleteAsync();
Console.WriteLine($"{ Environment.NewLine} database {_database.Id} deleted");
}
}
}
容器操作
创建容器(自动缩放 + 生存时间过期)
- .NET SDK v3
- .NET SDK v2
private static async Task CreateManualThroughputContainer(Database database)
{
// Set throughput to the minimum value of 400 RU/s manually configured throughput
string containerIdManual = ContainerName + "_Manual";
ContainerResponse container = await database.CreateContainerIfNotExistsAsync(
id: containerIdManual,
partitionKeyPath: partitionKeyPath,
throughput: 400);
}
// Create container with autoscale
private static async Task CreateAutoscaleThroughputContainer(Database database)
{
string autoscaleContainerId = ContainerName + "_Autoscale";
ContainerProperties containerProperties = new ContainerProperties(autoscaleContainerId, partitionKeyPath);
Container container = await database.CreateContainerIfNotExistsAsync(
containerProperties: containerProperties,
throughputProperties: ThroughputProperties.CreateAutoscaleThroughput(autoscaleMaxThroughput: 4000);
}
// Create a container with TTL Expiration
private static async Task CreateContainerWithTtlExpiration(Database database)
{
string containerIdManualwithTTL = ContainerName + "_ManualTTL";
ContainerProperties properties = new ContainerProperties
(id: containerIdManualwithTTL,
partitionKeyPath: partitionKeyPath);
properties.DefaultTimeToLive = (int)TimeSpan.FromDays(1).TotalSeconds; //expire in 1 day
ContainerResponse containerResponse = await database.CreateContainerIfNotExistsAsync(containerProperties: properties);
ContainerProperties returnedProperties = containerResponse;
}
读取容器属性
- .NET SDK v3
- .NET SDK v2
private static async Task ReadContainerProperties(Database database)
{
string containerIdManual = ContainerName + "_Manual";
Container container = database.GetContainer(containerIdManual);
ContainerProperties containerProperties = await container.ReadContainerAsync();
}
删除容器
- .NET SDK v3
- .NET SDK v2
private static async Task DeleteContainers(Database database)
{
string containerIdManual = ContainerName + "_Manual";
// Delete a container
await database.GetContainer(containerIdManual).DeleteContainerAsync();
// Delete all CosmosContainer resources for a database
using (FeedIterator<ContainerProperties> feedIterator = database.GetContainerQueryIterator<ContainerProperties>())
{
while (feedIterator.HasMoreResults)
{
foreach (ContainerProperties _container in await feedIterator.ReadNextAsync())
{
await database.GetContainer(_container.Id).DeleteContainerAsync();
Console.WriteLine($"{Environment.NewLine} deleted container {_container.Id}");
}
}
}
}
项目和查询操作
创建一个项目
- .NET SDK v3
- .NET SDK v2
private static async Task CreateItemAsync(Container container)
{
// Create a SalesOrder POCO object
SalesOrder salesOrder1 = GetSalesOrderSample("Account1", "SalesOrder1");
ItemResponse<SalesOrder> response = await container.CreateItemAsync(salesOrder1,
new PartitionKey(salesOrder1.AccountNumber));
}
private static async Task RunBasicOperationsOnDynamicObjects(Container container)
{
// Dynamic Object
dynamic salesOrder = new
{
id = "SalesOrder5",
AccountNumber = "Account1",
PurchaseOrderNumber = "PO18009186470",
OrderDate = DateTime.UtcNow,
Total = 5.95,
};
Console.WriteLine("\nCreating item");
ItemResponse<dynamic> response = await container.CreateItemAsync<dynamic>(
salesOrder, new PartitionKey(salesOrder.AccountNumber));
dynamic createdSalesOrder = response.Resource;
}
读取容器中的所有项
- .NET SDK v3
- .NET SDK v2
private static async Task ReadAllItems(Container container)
{
// Read all items in a container
List<SalesOrder> allSalesForAccount1 = new List<SalesOrder>();
using (FeedIterator<SalesOrder> resultSet = container.GetItemQueryIterator<SalesOrder>(
queryDefinition: null,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey("Account1"),
MaxItemCount = 5
}))
{
while (resultSet.HasMoreResults)
{
FeedResponse<SalesOrder> response = await resultSet.ReadNextAsync();
SalesOrder salesOrder = response.First();
Console.WriteLine($"\n1.3.1 Account Number: {salesOrder.AccountNumber}; Id: {salesOrder.Id}");
allSalesForAccount1.AddRange(response);
}
}
}
查询项
对 SqlQuerySpec 的更改(v3.0 SDK 中的 QueryDefinition)
SDK v2 中的 SqlQuerySpec 类现已在 SDK v3 中重命名为 QueryDefinition。
SqlParameterCollection 并且 SqlParameter 已被删除。 使用构建器模型将参数添加到QueryDefinition中QueryDefinition.WithParameter。 用户可以使用 QueryDefinition.GetQueryParameters 访问参数
- .NET SDK v3
- .NET SDK v2
private static async Task QueryItems(Container container)
{
// Query for items by a property other than Id
QueryDefinition queryDefinition = new QueryDefinition(
"select * from sales s where s.AccountNumber = @AccountInput")
.WithParameter("@AccountInput", "Account1");
List<SalesOrder> allSalesForAccount1 = new List<SalesOrder>();
using (FeedIterator<SalesOrder> resultSet = container.GetItemQueryIterator<SalesOrder>(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey("Account1"),
MaxItemCount = 1
}))
{
while (resultSet.HasMoreResults)
{
FeedResponse<SalesOrder> response = await resultSet.ReadNextAsync();
SalesOrder sale = response.First();
Console.WriteLine($"\n Account Number: {sale.AccountNumber}; Id: {sale.Id};");
allSalesForAccount1.AddRange(response);
}
}
}
删除项
- .NET SDK v3
- .NET SDK v2
private static async Task DeleteItemAsync(Container container)
{
ItemResponse<SalesOrder> response = await container.DeleteItemAsync<SalesOrder>(
partitionKey: new PartitionKey("Account1"), id: "SalesOrder3");
}
更改源查询
- .NET SDK v3
- .NET SDK v2
private static async Task QueryChangeFeedAsync(Container container)
{
FeedIterator<SalesOrder> iterator = container.GetChangeFeedIterator<SalesOrder>(ChangeFeedStartFrom.Beginning(), ChangeFeedMode.Incremental);
string continuation = null;
while (iterator.HasMoreResults)
{
FeedResponse<SalesOrder> response = await iteratorForTheEntireContainer.ReadNextAsync();
if (response.StatusCode == HttpStatusCode.NotModified)
{
// No new changes
continuation = response.ContinuationToken;
break;
}
else
{
// Process the documents in response
}
}
}
后续步骤
- 生成控制台应用 以使用 v3 SDK 管理 Azure Cosmos DB for NoSQL 数据
- 尝试为迁移到 Azure Cosmos DB 进行容量规划?
- 如果只知道现有数据库群集中的 vCore 和服务器数量,请阅读使用 vCore 或 vCPU 估算请求单位
- 若知道当前数据库工作负载的典型请求速率,请阅读使用 Azure Cosmos DB 容量计划工具估算请求单位