[英]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.