Overview
このガイドでは、 MongoDB Entity Framework コア プロバイダーを使用して、 MongoDBデータベース内のドキュメント間の関係をモデル化する方法を学習できます。 FS コア プロバイダーは、次の関係タイプをサポートしています。
埋め込み関係: 親ドキュメント内に直接埋め込まれたサブドキュメント。これは、 MongoDBの 1 対 1 および 1 対多の関係に推奨されるアプローチです。
手動参照:ドキュメントをサブドキュメントとして埋め込むのではなく、フィールドにIDを保存して別のドキュメントを参照するドキュメント。このアプローチは、多対多の関係のモデル化や、ドキュメントに単独でアクセスする必要がある場合に適しています。
Tip
クエリ パターンのスキーマを設計する
MongoDBのデータをモデル化するときは、まず、アプリケーションが最も頻繁に実行する操作を決定します。次に、それらの操作を効率的にサポートするドキュメント構造を選択します。 MongoDBで最も効率的なスキーマは、 リレーショナルデータベースに使用するスキーマと一致しない場合があります。代わりに、どのデータが一緒に取得されることが多いか、どの値が頻繁に変更されるか、またどのデータ関係が時間の経過とともに変化するかを考慮してください。
データ モデリングとリレーショナルデータベースからMongoDB Serverへの移行の詳細については、 MongoDB Serverドキュメントの「 SQLからMongoDBへのマッピング チャートとMongoDBのデータ モデリング 」を参照してください。
埋め込み関係
親ドキュメントにサブドキュメントを埋め込むには、埋め込みドキュメントのモデルを所有するエンティティとして指定します。埋め込み関係は、関係をモデル化し、一緒にアクセスする頻繁にアクセスするドキュメントのパフォーマンスを向上させるMongoDBネイティブの方法です。
ドキュメントを埋め込む場合
関連データが通常親のコンテキストで表示され、かつ埋め込みデータのサイズが適切に制限されている場合は、埋め込みドキュメントを使用します。これには、アドレス、設定、または最近使用されたアイテムの限定的なリストなど、子値の小規模なセットが含まれます。ドキュメントを埋め込むと、 MongoDB Server は1 回のデータベース操作で関連データを取得したり、単一のドキュメント内で関連データを不可分的に更新したりすることで、パフォーマンスを向上させることができます。
以下の場合は、データの埋め込みを検討してください。
アプリケーションは通常、親データと子データを一緒に読み取ります。 1 回のクエリですべてを取得すると、個別の 呼び出しを行うよりも効率的です。
関連データは、明確な上限がないと増加しません。境界のあるデータを使用しても、ドキュメントが無限に大きくなったり、MongoDB の 16MBドキュメントサイズ制限に達したりすることはありません。
関連データは、親とは独立して、頻繁に変更されることはありません。これにより、親ドキュメント の書き換えにかかるオーバーヘッドが少なくなります。
注意
ネストされた階層
ネストされた所有エンティティのプロパティを更新すると、FS コア プロバイダーは ルートドキュメント全体を書き換えます。 3 つ以上のレベルのネストを持つエンティティ階層では、ツリーの深い変更によって親ドキュメントの完全な書き換えがトリガーされるため、この書込みコストは複合化されます。モデルがディープ ネストを使用しており、アプリケーションが頻繁に書き込みを実行する場合は、階層をフラット化するか、内部所有エンティティを手動参照に置き換えることを検討してください。
詳細については、 MongoDB Serverドキュメントの「 埋め込みデータ 」を参照してください。
ドキュメントの埋め込み方法
所有 エンティティは、次の方法で指定できます。
[Owned] 属性:
[Owned]サブドキュメントのモデルクラスに データ注釈属性を適用します。このアプローチは宣言的で簡潔ですが、所有されている型のすべての使用にグローバルに適用されるため、柔軟ではありません。Flutter API :
OwnsOne()OwnsMany()DbContext構成で メソッドまたは メソッドを呼び出します。このアプローチではより制御と柔軟性が向上するため、 モデルクラス を変更しなくても、特定のエンティティに対して異なる方法で関係を構成できます。
次のセクションでは、単一および複数の所有者エンティティをモデル化するための、これらのアプローチの例を示します。
1 つの所有権を持つエンティティ
親ドキュメントに単一のドキュメントを埋め込むには、DbContext 構成で OwnsOne() メソッドを呼び出すか、埋め込みクラスに [Owned] 属性を適用します。
次の例では、埋め込み Addressドキュメントを含む Customer エンティティを示しています。対応する構文を確認するには、[Owned] Attribute タブまたは Fluent APIタブを選択します。
[] public class Address { public string Street { get; set; } = null!; public string City { get; set; } = null!; public string Country { get; set; } = null!; } public class Customer { public ObjectId Id { get; set; } public string Name { get; set; } = null!; public Address Address { get; set; } = null!; }
public class CustomerDbContext : DbContext { public DbSet<Customer> Customers { get; set; } = null!; public CustomerDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<Customer>(c => { c.OwnsOne(c => c.Address); c.ToCollection("customers"); }); } }
Customer エンティティを保存すると、 MongoDB はAddress を Customerドキュメント内の埋め込みドキュメントとして保存します。次のJSON は、 MongoDBでこの関係がどのように表示されるかを示しています。
{ "_id": ObjectId("..."), "Name": "John Doe", "Address": { "Street": "123 Main St", "City": "New York", "Country": "USA" } }
複数所有エンティティ
親ドキュメントに複数のドキュメントを埋め込むには、OwnsMany() メソッドを呼び出すか、埋め込みクラスに [Owned] 属性を適用し、コレクションプロパティを定義します。
次の例では、複数の埋め込み Order ドキュメントを含む Customer エンティティを示しています。対応する構文を確認するには、[Owned] Attribute タブまたは Fluent APIタブを選択します。
[] public class Order { public string Product { get; set; } = null!; public int Quantity { get; set; } } public class CustomerWithOrders { public ObjectId Id { get; set; } public string Name { get; set; } = null!; public List<Order> Orders { get; set; } = new(); }
public class OrderDbContext : DbContext { public DbSet<CustomerWithOrders> Customers { get; set; } = null!; public OrderDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity<CustomerWithOrders>(c => { c.OwnsMany(c => c.Orders); c.ToCollection("customers"); }); } }
CustomerWithOrders エンティティを保存すると、 MongoDB は次のJSONに示すように、Orders を埋め込みドキュメントの配列として保存します。
{ "_id": ObjectId("..."), "Name": "Jane Smith", "Orders": [ { "Product": "Laptop", "Quantity": 1 }, { "Product": "Mouse", "Quantity": 2 } ] }
手動参照
埋め込まれていないエンティティ間の関係をモデル化するには、あるドキュメントのIDを別のドキュメントのフィールドに保存することで、参照を手動で保存できます。このアプローチでは、アプリケーションコード内で関係を管理する必要があります。
参照を使用する場合
親とは別個に関連データを定期的にクエリする場合、子セットが非常に大きくなる可能性がある場合、または関連データが頻繁に変更される場合は、参照を使用してください。
次の場合は、参照の使用を検討してください。
関連データは、明確な上限なしに増加します。無制限配列はパフォーマンスの問題を発生させる可能性があり、MongoDB の 16MBドキュメントサイズ制限に達する可能性があります。
関連エンティティを単独でクエリする必要があります。親ドキュメントをスキャンせずに、子コレクションを直接クエリしてインデックスできます。
関連データは、親とは独立して頻繁に変更されます。埋め込みデータを頻繁に更新するには親ドキュメント全体を書き直す必要があるため、非効率的です。
アプリケーションは通常、親データと子データを一緒に読み取りはしません。参照を使用することで、不要な子データの取得を回避できます。
関係が多対多またはそれ以前の複雑な場合
データを重複させると、更新オーバーヘッドが大きくなりすぎます。
詳細については、 MongoDB Serverドキュメントの「 リファレンス データ 」を参照してください。
参照の使用方法
次の例は、関連エンティティへの参照を保存する方法を示しています。
public class Author { public ObjectId Id { get; set; } public string Name { get; set; } = null!; } public class Book { public ObjectId Id { get; set; } public string Title { get; set; } = null!; // Store reference to Author by storing the Author's Id public ObjectId AuthorId { get; set; } }
関連エンティティを検索するには、次の例に示すように、参照先のコレクションを個別にクエリする必要があります。
// Query a book and its author var book = db.Books.FirstOrDefault(b => b.Title == "My Book"); var author = db.Authors.FirstOrDefault(a => a.Id == book!.AuthorId);
1 対多の大きなセットにはサブセット パターンを使用する
親ドキュメントに大きな配列が含まれているが、アプリケーションが通常、最新のアイテムまたは最も頻繁にアクセスするアイテムのみを使用する場合は、 サブセット パターン を検討してください。
サブセット パターンでは、最も頻繁にアクセスされるアイテムをメインドキュメントに保持し、残りを別のコレクションに移動します。このアプローチにより、ドキュメントサイズを縮小し、より多くのワーキングセットをRAMに保持でき、クエリのパフォーマンスを向上させることができます。
このパターンは、埋め込みのパフォーマンス上の利点を必要としているが、埋め込み履歴が完全に埋め込まれていると、ドキュメントが大きくなりすぎたり、ロードコストが高すぎたりする場合に便利です。
詳細情報
MongoDBでのスキーマ設計のベストプラクティスの詳細については、 MongoDB Serverドキュメントの「 MongoDBでのデータモデリングのベストプラクティス 」と「 スキーマの設計 」を参照してください。
MongoDBでデータをモデリングする方法の詳細については、 MongoDB Serverドキュメントの「 データ モデリング 」を参照してください。
Entity Framework コアで所有されているエンティティタイプの詳細については、 Microsoft Entity Framework コア ドキュメントの「 所有されたエンティティタイプ 」を参照してください。
API ドキュメント
このガイドで説明されているメソッドとタイプの詳細については、次のMicrosoft APIドキュメントを参照してください。