簡體   English   中英

如何使用 .NET Core 2.1 和 Stream API 在 Cosmos DB 中批量插入

[英]How to Bulk Insert in Cosmos DB with .NET Core 2.1 and Stream API

我正在嘗試使用此 CosmosDB 示例實現批量插入。 此示例使用 .NET Core 3.* 創建並支持 System.Text.Json。

使用 CreateItemAsync 方法時,它工作得很好:

    var concurrentTasks = new List<Task<ItemResponse<Notification>>>();
    foreach (var entity in entities)
    {
        entity.Id = GenerateId(entity);

        var requestOptions = new ItemRequestOptions();
        requestOptions.EnableContentResponseOnWrite = false; // We don't need to get the entire body returend.
        concurrentTasks.Add(Container.CreateItemAsync(entity, new PartitionKey(entity.UserId), requestOptions));
    }

    await Task.WhenAll(concurrentTasks);

但是,我想看看是否可以通過將數據直接流式傳輸到 CosmosDB 來減少 RU 的數量,希望 CosmosDB 不會因為反序列化 JSON 本身而向我收費。

我在 .NET Core 2.1 和 Newtonsoft.Json 中工作。 這是我的代碼,它不返回成功的狀態代碼。 響應頭中的子狀態碼為“0”。

    Notification[] notifications = entities.ToArray();
    var itemsToInsert = new Dictionary<PartitionKey, Stream>();

    foreach (var notification in notifications)
    {
        MemoryStream ms = new MemoryStream();
        StreamWriter writer = new StreamWriter(ms);
        JsonTextWriter jsonWriter = new JsonTextWriter(writer);
        JsonSerializer ser = new JsonSerializer();
                
        ser.Serialize(jsonWriter, notification);

        await jsonWriter.FlushAsync();
        await writer.FlushAsync();

        itemsToInsert.Add(new PartitionKey(notification.UserId), ms);
    }

    List<Task> tasks = new List<Task>(notifications.Length);
    foreach (KeyValuePair<PartitionKey, Stream> item in itemsToInsert)
    {
        tasks.Add(Container.CreateItemStreamAsync(item.Value, item.Key)
            .ContinueWith((Task<ResponseMessage> task) =>
            {
                using (ResponseMessage response = task.Result)
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        Console.WriteLine($"Received {response.StatusCode} ({response.ErrorMessage}).");
                    }
                    else
                    {
                    }
                }
            }));
    }

    // Wait until all are done
    await Task.WhenAll(tasks);

response.StatusCode: BadRequest response.ErrorMessage: null

我假設我沒有以正確的方式序列化到 Stream 中。 有人有線索嗎?

更新

我發現新的 System.Text.Json 包也實現了 .NET Standard 2.0,所以我從 NUget 安裝了它。 現在我可以從前面提到的 Github 復制示例代碼。

        Notification[] notifications = entities.ToArray();
        var itemsToInsert = new List<Tuple<PartitionKey, Stream>>();

        foreach (var notification in notifications)
        {
            notification.id = $"{notification.UserId}:{Guid.NewGuid()}";

            MemoryStream stream = new MemoryStream();
            await JsonSerializer.SerializeAsync(stream, notification);

            itemsToInsert.Add(new Tuple<PartitionKey, Stream>(new PartitionKey(notification.RoleId), stream));
        }

        List<Task> tasks = new List<Task>(notifications.Length);
        foreach (var item in itemsToInsert)
        {
            tasks.Add(Container.CreateItemStreamAsync(item.Item2, item.Item1)
                .ContinueWith((Task<ResponseMessage> task) =>
                {
                    using (ResponseMessage response = task.Result)
                    {
                        if (!response.IsSuccessStatusCode)
                        {
                            Console.WriteLine($"Received {response.StatusCode} ({response.ErrorMessage}).");
                        }
                        else
                        {
                        }
                    }
                }));
        }

        // Wait until all are done
        await Task.WhenAll(tasks);

我仔細檢查了 BulkInsert 是否已啟用(否則第一種方法也不起作用)。 仍然有一個 BadRequest 和一個 NULL 用於 errorMessage。

我還檢查了數據沒有添加到容器中,盡管有 BadRequest。

我發現了問題。

我已經使用以下選項設置了我的 Cosmos 上下文:

var cosmosSerializationOptions = new CosmosSerializationOptions();
cosmosSerializationOptions.PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase;

CosmosClientOptions cosmosClientOptions = new CosmosClientOptions();
cosmosClientOptions.SerializerOptions = cosmosSerializationOptions;

因此CamelCase約定。 在我的第一個(工作)代碼示例中,我會讓 CosmosDB 上下文反序列化為 JSON。 他將使用此 CamelCase 約定進行序列化,因此我的 PartionKey UserId將被序列化為userId

但是,為了減少一些 RU,我將使用讓我負責序列化的CreateItemStreamAsync 還有一個錯誤,我的財產被定義為:

public int UserId { get; set; }

所以他會被序列化為 json UserId: 1

但是,分區鍵定義為/userId 因此,如果我添加 JsonPropertyName 屬性,它會起作用:

[JsonPropertyName("userId")]
public int UserId { get; set; } 

...如果只有錯誤消息會告訴我。

使用此CreateItemStream方法可節省大約 3% 的 RU。 但是,隨着時間的推移,我猜這會慢慢地節省一些 RU。

看起來流不可讀。 因此,錯誤的請求。 我會對MemoryStream的創建方式做一點修改:

foreach (var notification in notifications)
    {
        
        itemsToInsert.Add(new PartitionKey(notification.UserId), new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(notification))));
    }

當然,我將 Newtonsoft.json 用於 jsonConvert。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM