练习 - 创建新项

已完成

回想一下,你可以使用 Azure Cosmos DB .NET SDK 在容器中创建项。 对于此项目,产品容器包含每个类别的各个产品项和特殊类别项。 在此应用程序中需要处理两种情况:

  • 如果某个类别为空,则可以单独创建该类别的项。 没有要创建的相关产品项。
  • 但是,如果某个类别包含相关产品,则需要同时创建类别项和相关产品项。

现在,有两个关键要求:

  1. 以单个操作的形式单独创建项
  2. 使用事务性批处理创建多个相关项

Illustration of icons indicating data being uploaded to the cloud.

完成本练习后,项目将具有在容器中单独或批量创建项的逻辑。

将单个项添加到容器

在 Azure Cosmos DB 中,可以在容器中创建、替换或更新插入项。 创建项要求该项具有唯一标识符。 替换项要求该项已存在。 更新插入是两全其美的方法,它将检查唯一标识符,然后替换或创建项。 在本项目中,你希望能够多次运行应用而不会出错,这使得更新插入成为一个明确的选择。 对于第一个项,我们将创建没有关联产品的类别。 在这里,你将使用手动创建的类别实现单个更新插入操作。

  1. 再次打开 Program.cs 文件。

  2. 使用以下值创建一个名为 goggles 的新 Category 实例:

    属性
    id ef7fa0f1-0e9d-4435-aaaf-a778179a94ad
    categoryId gear-snow-goggles
    Category goggles = new(
        Id: "ef7fa0f1-0e9d-4435-aaaf-a778179a94ad",
        CategoryId: "gear-snow-goggles"
    );
    
  3. 使用与前面创建的 Category 实例的 categoryId 属性相同的值创建新的 PartitionKey 实例。

    PartitionKey gogglesKey = new("gear-snow-goggles");
    
  4. 使用 UpsertItemAsync 方法创建或替换传递给要创建项的对象和分区键值的项。

    Category result = await container.UpsertItemAsync(goggles, gogglesKey);
    
  5. result 的各种属性打印到控制台,包括:项的唯一标识符和项的类型。

    Console.WriteLine($"[New item created]:\t{result.Id}\t(Type: {result.Type})");
    
  6. 使用以下值创建一个名为 helmets 的新 Category 实例:

    属性
    id 91f79374-8611-4505-9c28-3bbbf1aa7df7
    categoryId gear-climb-helmets
    Category helmets = new(
        Id: "91f79374-8611-4505-9c28-3bbbf1aa7df7",
        CategoryId: "gear-climb-helmets"
    );
    
  7. 使用与前面创建的 Category 实例的 categoryId 属性相同的值创建新的 PartitionKey 实例。

    PartitionKey helmetsKey = new("gear-climb-helmets");
    
  8. 使用 UpsertItemAsync 方法创建或替换项。 传入要创建项的对象和分区键值。 返回 ItemResponse<T> 类型的对象。

    ItemResponse<Category> response = await container.UpsertItemAsync(helmets, helmetsKey);
    
  9. response 的各种属性打印到控制台,包括:基础项的唯一标识符、基础项的类型以及 RU 中的请求费用。

    Console.WriteLine($"[New item created]:\t{response.Resource.Id}\t(Type: {response.Resource.Type})\t(RUs: {response.RequestCharge})");
    
  10. 保存 Program.cs 文件。

以事务性批处理的形式实现多个操作

现在,请考虑要创建多个产品以及一个类别的场景。 如果已创建产品,但类别不存在,那么这些产品并不十分有用。 在创建多个项的情况下,你可以使用事务将多个“点”操作组合在一起,以便它们作为单个内聚单元都成功或失败。 返回到我们的场景,我们需要创建一个包含一些帐篷产品的户外帐篷类别。 我们已有一个没有任何产品项的单个类别项。 最终结果如下:

Diagram of items in Azure Cosmos DB grouped by their partition key.

在本节中,我们将创建一个事务性批处理,以同时创建 tents 类别和相关产品。

  1. 在 Program.cs 中,使用以下值创建一个名为 tents 的新 Category 实例:

    Property
    id 5df21ec5-813c-423e-9ee9-1a2aaead0be4
    categoryId gear-camp-tents
    Category tents = new(
        Id: "5df21ec5-813c-423e-9ee9-1a2aaead0be4",
        CategoryId: "gear-camp-tents"
    );
    
  2. 使用这些值创建四个 Product 类型的实例。

    属性 cirroa kuloar mammatin nimbolo
    Id e8dddee4-9f43-4d15-9b08-0d7f36adcac8 e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa f7653468-c4b8-47c9-97ff-451ee55f4fd5 6e3b7275-57d4-4418-914d-14d1baca0979
    CategoryId gear-camp-tents gear-camp-tents gear-camp-tents gear-camp-tents
    名称 Cirroa Tent Kuloar Tent Mammatin Tent Nimbolo Tent
    价格 490.00 530.00 0.00 330.00
    Archived false false true false
    数量 15 8 0 35
    Product cirroa = new(
        Id: "e8dddee4-9f43-4d15-9b08-0d7f36adcac8",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Cirroa Tent",
        Price = 490.00m,
        Archived = false,
        Quantity = 15
    };
    
    Product kuloar = new(
        Id: "e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Kuloar Tent",
        Price = 530.00m,
        Archived = false,
        Quantity = 8
    };
    
    Product mammatin = new(
        Id: "f7653468-c4b8-47c9-97ff-451ee55f4fd5",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Mammatin Tent",
        Price = 0.00m,
        Archived = true,
        Quantity = 0
    };
    
    Product nimbolo = new(
        Id: "6e3b7275-57d4-4418-914d-14d1baca0979",
        CategoryId: "gear-camp-tents"
    ){
        Name = "Nimbolo Tent",
        Price = 330.00m,
        Archived = false,
        Quantity = 35
    };
    
  3. 现在,使用 gear-camp-tents 值创建一个新的 PartitionKey 实例。

    PartitionKey tentsKey = new("gear-camp-tents");
    
  4. 使用 CreateTransactionalBatch(PartitionKey) 方法创建范围限定为 gear-camp-tents 分区键值的新事务性批处理。 使用 Fluent 语法,添加五个更新插入操作,以在容器中为类别和所有相关产品创建所需的项。

    TransactionalBatch batch = container.CreateTransactionalBatch(tentsKey)
        .UpsertItem<Category>(tents)
        .UpsertItem<Product>(cirroa)
        .UpsertItem<Product>(kuloar)
        .UpsertItem<Product>(mammatin)
        .UpsertItem<Product>(nimbolo);
    
  5. 向控制台输出一条消息,指示我们正在启动批处理操作。

    Console.WriteLine("[Batch started]");
    
  6. 使用 TransactionalBatch.ExecuteAsync 方法执行批处理并返回特殊响应类型。

    using TransactionalBatchResponse batchResponse = await batch.ExecuteAsync();
    
  7. 使用 for 循环,循环访问响应中的所有项。 首先,使用 Item 基类作为泛型将每个项转换为 TransactionalBatchOperationResult 类型。 然后,打印响应对象的唯一标识符和类型。

    for (int i = 0; i < batchResponse.Count; i++)
    {
        TransactionalBatchOperationResult<Item> batchResult = batchResponse.GetOperationResultAtIndex<Item>(i);
        Console.WriteLine($"[New item created]:\t{batchResult.Resource.Id}\t(Type: {batchResult.Resource.Type})");
    }
    
  8. 向控制台输出另一条消息,指示批处理已完成。 在此消息中包含整个批处理的请求费用。

    Console.WriteLine($"[Batch completed]:\t(RUs: {batchResponse.RequestCharge})");
    
  9. 保存 Program.cs 文件。

检查你的工作

现在,应用将创建多个项,且设计为具有足够的弹性,以便多次运行,而不会引发异常。 在这里,你将运行应用程序,并检查输出中六个新创建的项是否都有唯一标识符。

  1. 在终端中运行 .NET 应用程序:

    dotnet run
    
  2. 观察运行应用程序的输出。 输出应匹配此处的示例:

    ...
    [New item created]:     ef7fa0f1-0e9d-4435-aaaf-a778179a94ad    (Type: Category)
    [New item created]:     91f79374-8611-4505-9c28-3bbbf1aa7df7    (Type: Category)        (RUs: 10.29)
    [Batch started]
    [New item created]:     5df21ec5-813c-423e-9ee9-1a2aaead0be4    (Type: Category)
    [New item created]:     e8dddee4-9f43-4d15-9b08-0d7f36adcac8    (Type: Product)
    [New item created]:     e6f87b8d-8cd7-4ade-a005-14d3e2fbd1aa    (Type: Product)
    [New item created]:     f7653468-c4b8-47c9-97ff-451ee55f4fd5    (Type: Product)
    [New item created]:     6e3b7275-57d4-4418-914d-14d1baca0979    (Type: Product)
    [Batch completed]:      (RUs: 36.76)
    

    提示

    本示例输出中显示的 RU 可能与输出不同。