EF Core Azure Cosmos DB プロバイダーでの非構造化データの操作

EF Core は、モデルで定義されたスキーマに従うデータを簡単に操作できるように設計されています。 ただし、Azure Cosmos DB の長所の 1 つは、格納されるデータの形状の柔軟性です。

生の JSON へのアクセス

"__jObject" シャドウ プロパティは EF Core 11 で削除されました。 詳細については、「 EF Core 11 の破壊的変更 」を参照してください。

EF Core 10 以前では、ストアから受信したデータと格納されるデータを表すJObjectを含む、"__jObject"という名前のシャドウ状態の特殊なプロパティを使用して、EF Core によって追跡されないプロパティにアクセスできるようになりました。

using (var context = new OrderContext())
{
    await context.Database.EnsureDeletedAsync();
    await context.Database.EnsureCreatedAsync();

    var order = new Order
    {
        Id = 1, ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" }, PartitionKey = "1"
    };

    context.Add(order);

    await context.SaveChangesAsync();
}

using (var context = new OrderContext())
{
    var order = await context.Orders.FirstAsync();
    var orderEntry = context.Entry(order);

    var jsonProperty = orderEntry.Property<JObject>("__jObject");
    jsonProperty.CurrentValue["BillingAddress"] = "Clarence House";

    orderEntry.State = EntityState.Modified;

    await context.SaveChangesAsync();
}

using (var context = new OrderContext())
{
    var order = await context.Orders.FirstAsync();
    var orderEntry = context.Entry(order);
    var jsonProperty = orderEntry.Property<JObject>("__jObject");

    Console.WriteLine($"First order will be billed to: {jsonProperty.CurrentValue["BillingAddress"]}");
}
{
    "Id": 1,
    "PartitionKey": "1",
    "TrackingNumber": null,
    "id": "1",
    "Address": {
        "ShipsToCity": "London",
        "ShipsToStreet": "221 B Baker St"
    },
    "_rid": "eLMaAK8TzkIBAAAAAAAAAA==",
    "_self": "dbs/eLMaAA==/colls/eLMaAK8TzkI=/docs/eLMaAK8TzkIBAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-683e-0a12bf8d01d5\"",
    "_attachments": "attachments/",
    "BillingAddress": "Clarence House",
    "_ts": 1568164374
}

Warnung

"__jObject" プロパティは EF Core インフラストラクチャの一部でした。 EF Core 10 以前にのみ存在し、EF Core 11 以降では削除されています。

CosmosClient の使用

EF Core から完全に分離するには、Azure Cosmos DB SDK の一部である CosmosClient オブジェクトをDbContextから取得します。

using (var context = new OrderContext())
{
    var cosmosClient = context.Database.GetCosmosClient();
    var database = cosmosClient.GetDatabase("OrdersDB");
    var container = database.GetContainer("Orders");

    var resultSet = container.GetItemQueryIterator<JObject>(new QueryDefinition("select * from o"));
    var order = (await resultSet.ReadNextAsync()).First();

    Console.WriteLine($"First order JSON: {order}");

    order.Remove("TrackingNumber");

    await container.ReplaceItemAsync(order, order["id"].ToString());
}

プロパティ値がありません

前の例では、 "TrackingNumber" プロパティを注文から削除しました。 Azure Cosmos DB でのインデックス作成のしくみにより、プロジェクション内以外の場所で不足しているプロパティを参照するクエリでは、予期しない結果が返される可能性があります。 例えば次が挙げられます。

using (var context = new OrderContext())
{
    var orders = await context.Orders.ToListAsync();
    var sortedOrders = await context.Orders.OrderBy(o => o.TrackingNumber).ToListAsync();

    Console.WriteLine($"Number of orders: {orders.Count}");
    Console.WriteLine($"Number of sorted orders: {sortedOrders.Count}");
}

並べ替えられたクエリは、実際には結果を返しません。 つまり、ストアを直接操作するときは、EF Core によってマップされるプロパティを常に設定するように注意する必要があります。

この動作は、将来のバージョンの Azure Cosmos DB で変更される可能性があります。 たとえば、インデックス作成ポリシーで複合インデックス {Id/? が定義されている場合などです。 ASC、TrackingNumber/? ASC)}、'ORDER BY c.Id ASC、c.Discriminator ASC ' を 持つクエリは、 "TrackingNumber" プロパティがない項目を返します。