迁移应用程序以使用 Azure Cosmos DB .NET SDK v3Migrate your application to use the Azure Cosmos DB .NET SDK v3

适用于: SQL API

重要

若要了解 Azure Cosmos DB .NET SDK v3,请参阅发行说明.NET GitHub 存储库、.NET SDK v3 性能提示故障排除指南To learn about the Azure Cosmos DB .NET SDK v3, see the Release notes, the .NET GitHub repository, .NET SDK v3 Performance Tips, and the Troubleshooting guide.

本文重点介绍将现有 .NET 应用程序升级到适用于 Core (SQL) API 的较新 Azure Cosmos DB .NET SDK v3 时的一些注意事项。This article highlights some of the considerations of upgrading your existing .NET application to the newer Azure Cosmos DB .NET SDK v3 for Core (SQL) API. Azure Cosmos DB .NET SDK v3 对应于 Microsoft.Azure.Cosmos 命名空间。Azure Cosmos DB .NET SDK v3 corresponds to the Microsoft.Azure.Cosmos namespace. 如果要从以下任何 Azure Cosmos DB .NET SDK 迁移应用程序,则可以使用此文档中提供的信息:You can use the information provided in this doc if you are migrating your application from any of the following Azure Cosmos DB .NET SDKs:

  • 适用于 SQL API 的 Azure Cosmos DB .NET Framework SDK v2Azure Cosmos DB .NET Framework SDK v2 for SQL API
  • 适用于 SQL API 的 Azure Cosmos DB .NET Core SDK v2Azure Cosmos DB .NET Core SDK v2 for SQL API

本文中的说明还有助于迁移以下外部库,这些库现在是适用于 Core (SQL) API 的 Azure Cosmos DB .NET SDK v3 的一部分:The instructions in this article also help you to migrate the following external libraries that are now part of the Azure Cosmos DB .NET SDK v3 for Core (SQL) API:

  • .NET 更改源处理器库 2.0.NET change feed processor library 2.0
  • .NET 批量执行工具库 1.1 或更高版本.NET bulk executor library 1.1 or greater

.NET V3 SDK 的新增功能What's new in the .NET V3 SDK

v3 SDK 包含许多可用性和性能改进,包括:The v3 SDK contains many usability and performance improvements, including:

  • 直观编程模型命名Intuitive programming model naming
  • .NET Standard 2.0 **.NET Standard 2.0 **
  • 通过流 API 支持提高了性能Increased performance through stream API support
  • 取代 URI 工厂需求的 fluent 层次结构Fluent hierarchy that replaces the need for URI factory
  • 对更改源处理器库的内置支持Built-in support for change feed processor library
  • 对批量操作的内置支持Built-in support for bulk operations
  • 用于简化单元测试的 Mockabale APIMockabale APIs for easier unit testing
  • 事务性批处理和 Blazor 支持Transactional batch and Blazor support
  • 可插入序列化程序Pluggable serializers
  • 缩放非分区和自动缩放容器Scale non-partitioned and autoscale containers

**该 SDK 面向 .NET Standard 2.0,.NET Standard 2.0 将现有 Azure Cosmos DB .NET Framework 和 .NET Core SDK 统一到单个 .NET SDK 中。** The SDK targets .NET Standard 2.0 that unifies the existing Azure Cosmos DB .NET Framework and .NET Core SDKs into a single .NET SDK. 可以在任何实现 .NET Standard 2.0 的平台(包括 .NET Framework 4.6.1+ 和 .NET Core 2.0+ 应用程序)中使用 .NET SDK。You can use the .NET SDK in any platform that implements .NET Standard 2.0, including your .NET Framework 4.6.1+ and .NET Core 2.0+ applications.

大多数网络、重试逻辑和较低级别的 SDK 在很大程度上保持不变。Most of the networking, retry logic, and lower levels of the SDK remain largely unchanged.

Azure Cosmos DB .NET SDK v3 现在为开源。The Azure Cosmos DB .NET SDK v3 is now open source. 欢迎提出任何拉取请求,我们将在 GitHub 上记录问题并跟踪反馈。We welcome any pull requests and will be logging issues and tracking feedback on GitHub. 我们致力于提供任何能够改善客户体验的功能。We'll work on taking on any features that will improve customer experience.

为何要迁移到 .NET v3 SDKWhy migrate to the .NET v3 SDK

除了大量的可用性和性能改进之外,最新 SDK 中的新功能投资将不会反向移植到较旧版本。In addition to the numerous usability and performance improvements, new feature investments made in the latest SDK will not be back ported to older versions.

尽管现在还没有计划停用对 2.0 SDK 的支持,但 SDK 会在将来被较新版本替代,该 SDK 将进入维护模式。While there are no immediate plans to retire support for the 2.0 SDKs, the SDKs will be replaced by newer versions in the future and the SDK will go into maintenance mode. 如需获得最佳的开发体验,我们建议务必从最新的受支持的 SDK 版本入手。For the best development experience, we recommend always starting with the latest supported version of SDK.

主名称从 v2 SDK 更改为 v3 SDKMajor name changes from v2 SDK to v3 SDK

在整个 .NET 3.0 SDK 中应用了以下名称更改,与 Core (SQL) API 的 API 命名约定保持一致:The following name changes have been applied throughout the .NET 3.0 SDK to align with the API naming conventions for the Core (SQL) API:

  • DocumentClient 已重命名为 CosmosClientDocumentClient is renamed to CosmosClient
  • Collection 已重命名为 ContainerCollection is renamed to Container
  • Document 已重命名为 ItemDocument is renamed to Item

所有资源对象都是用附加属性重命名的,为清楚起见,其中包含资源名称。All the resource objects are renamed with additional properties, which, includes the resource name for clarity.

下面是一些主要的类名更改:The following are some of the main class name changes:

.NET v2 SDK.NET v2 SDK .NET v3 SDK.NET v3 SDK
Microsoft.Azure.Documents.Client.DocumentClient Microsoft.Azure.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

.NET v3 SDK 上被替换的类Classes replaced on .NET v3 SDK

在 3.0 SDK 上,以下类已被替换:The following classes have been replaced on the 3.0 SDK:

  • Microsoft.Azure.Documents.UriFactory

  • Microsoft.Azure.Documents.Document

  • Microsoft.Azure.Documents.Resource

Microsoft.Azure.Documents.UriFactory 类已被替换为 fluent 设计。The Microsoft.Azure.Documents.UriFactory class has been replaced by the fluent design. Fluent 设计在内部生成 URL,并支持传递单个 Container 对象,而不是 DocumentClientDatabaseNameDocumentCollectionThe fluent design builds URLs internally and allows a single Container object to be passed around instead of a DocumentClient, DatabaseName, and DocumentCollection.

对项 ID 生成的更改Changes to item ID generation

.NET v3 SDK 中不再自动填充项 ID。Item ID is no longer auto populated in the .NET v3 SDK. 因此,项 ID 必须具体包括生成的 ID。Therefore, the Item ID must specifically include a generated ID. 查看以下示例:View the following example:

[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }

更改了连接模式的默认行为Changed default behavior for connection mode

SDK v3 现在默认为直接 + TCP 连接模式,而以前的 v2 SDK 默认为网关 + HTTPS 连接模式。The SDK v3 now defaults to Direct + TCP connection modes compared to the previous v2 SDK, which defaulted to Gateway + HTTPS connections modes. 此更改增强了性能和可伸缩性。This change provides enhanced performance and scalability.

对 FeedOptions 的更改(v3.0 SDK 中的 QueryRequestOptions)Changes to FeedOptions (QueryRequestOptions in v3.0 SDK)

SDK v2 中的 FeedOptions 类现已在 SDK v3 中被重命名为 QueryRequestOptions,在此类中,多个属性的名称和/或默认值已更改,或者已完全删除。The FeedOptions class in SDK v2 has now been renamed to QueryRequestOptions in the SDK v3 and within the class, several properties have had changes in name and/or default value or been removed completely.

FeedOptions.MaxDegreeOfParallelism 已重命名为 QueryRequestOptions.MaxConcurrency,默认值和关联行为保持不变,并行查询执行期间运行客户端的操作将以非并行方式串行执行。FeedOptions.MaxDegreeOfParallelism has been renamed to QueryRequestOptions.MaxConcurrency and default value and associated behavior remains the same, operations run client side during parallel query execution will be executed serially with no-parallelism.

FeedOptions.EnableCrossPartitionQuery 已删除,SDK 3.0 中的默认行为是将执行跨分区查询,而不需要专门启用属性。FeedOptions.EnableCrossPartitionQuery has been removed and the default behavior in SDK 3.0 is that cross-partition queries will be executed without the need to enable the property specifically.

默认情况下启用 FeedOptions.PopulateQueryMetrics,结果显示在响应的诊断属性中。FeedOptions.PopulateQueryMetrics is enabled by default with the results being present in the diagnostics property of the response.

FeedOptions.RequestContinuation 现已提升为查询方法本身。FeedOptions.RequestContinuation has now been promoted to the query methods themselves.

已删除以下属性:The following properties have been removed:

  • FeedOptions.DisableRUPerMinuteUsage

  • FeedOptions.EnableCrossPartitionQuery

  • FeedOptions.JsonSerializerSettings

  • FeedOptions.PartitionKeyRangeId

  • FeedOptions.PopulateQueryMetrics

构造客户端Constructing a client

.NET SDK v3 提供取代 SDK v2 URI 工厂需求的 fluent CosmosClientBuilder 类。The .NET SDK v3 provides a fluent CosmosClientBuilder class that replaces the need for the SDK v2 URI Factory.

下面的示例创建一个新 CosmosClientBuilder,其中包含强 ConsistencyLevel 和首选位置列表:The following example creates a new CosmosClientBuilder with a strong ConsistencyLevel and a list of preferred locations:

CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder(
    accountEndpoint: "https://testcosmos.documents.azure.cn:443/",
    authKeyOrResourceToken: "SuperSecretKey")
.WithConsistencyLevel(ConsistencyLevel.Strong)
.WithApplicationRegion(Regions.ChinaEast);
CosmosClient client = cosmosClientBuilder.Build();

直接从 v3 SDK 使用更改源处理器 APIUsing the change feed processor APIs directly from the v3 SDK

v3 SDK 具有对更改源处理器 API 的内置支持,支持使用同一 SDK 来生成应用程序和更改源处理器实现。The v3 SDK has built-in support for the Change Feed Processor APIs, allowing you use the same SDK for building your application and change feed processor implementation. 以前,必须使用单独的更改源处理器库。Previously, you had to use a separate change feed processor library.

有关详细信息,请参阅如何从更改源处理器库迁移到 Azure Cosmos DB .NET v3 SDKFor more information, see how to migrate from the change feed processor library to the Azure Cosmos DB .NET v3 SDK

直接从 V3 SDK 使用批量执行工具库Using the bulk executor library directly from the V3 SDK

v3 SDK 具有对批量执行工具库的内置支持,支持使用同一 SDK 来生成应用程序和执行批量操作。The v3 SDK has built-in support for the bulk executor library, allowing you to use the same SDK for building your application and performing bulk operations. 以前,需要使用单独的批量执行工具库。Previously, you were required to use a separate bulk executor library.

有关详细信息,请参阅如何从批量执行工具库迁移到 Azure Cosmos DB .NET V3 SDK 中的批量操作支持For more information, see how to migrate from the bulk executor library to bulk support in Azure Cosmos DB .NET V3 SDK

代码片段比较Code snippet comparisons

以下代码片段显示了 .NET v2 和 v3 SDK 在资源创建方式上的差异:The following code snippet shows the differences in how resources are created between the .NET v2 and v3 SDKs:

数据库操作Database operations

创建数据库Create a database

// 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 读取数据库Read a database by ID

// 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()}");
        }
    }
}

删除数据库Delete a database

// 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");
        }
    }
}

容器操作Container operations

创建容器(自动缩放 + 生存时间(包含到期时间))Create a container (Autoscale + Time to live with expiration)

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;
}

读取容器属性Read container properties

private static async Task ReadContainerProperties(Database database)
{
    string containerIdManual = ContainerName + "_Manual";
    Container container = database.GetContainer(containerIdManual);
    ContainerProperties containerProperties = await container.ReadContainerAsync();
}

删除容器Delete a container

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}");
            }
        }
    }
}

项和查询操作Item and query operations

创建项Create an item

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;
}

读取容器中的所有项Read all the items in a container

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);
        }
    }
}

查询项Query items

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);
        }
    }
}

删除项Delete an item

private static async Task DeleteItemAsync(Container container)
{
    ItemResponse<SalesOrder> response = await container.DeleteItemAsync<SalesOrder>(
        partitionKey: new PartitionKey("Account1"), id: "SalesOrder3");
}

后续步骤Next steps