Overview
In this guide, you can learn how to use the MongoDB Entity Framework Core Provider to model relationships among documents in your MongoDB database. The EF Core Provider supports the following relationship types:
Embedded Relationships: Subdocuments embedded directly within a parent document. This is the recommended approach for one-to-one and one-to-many relationships in MongoDB.
Manual References: A document that references another document by storing its ID in a field, instead of embedding the document as a subdocument. This approach works well for modeling many-to-many relationships or when you need to access the documents independently.
Tip
Design Your Schema for Query Patterns
When you model data for MongoDB, first determine the operations that your application runs most often. Then, choose a document structure that supports those operations efficiently. The most efficient schema in MongoDB might not match the schema that you would use for a relational database. Instead, consider which data is commonly retrieved together, which values change frequently, and which data relationships change over time.
To learn more about data modeling and migrating from a relational database to MongoDB Server, see the SQL to MongoDB Mapping Chart and Data Modeling in MongoDB in the MongoDB Server documentation.
Embedded Relationships
To embed a subdocument in a parent document, specify the model for the embedded document as an owned entity. Embedded relationships are the MongoDB-native way to model relationships and offer better performance for documents that you frequently access together.
When to Embed Documents
Use embedded documents when the related data is usually viewed in the context of its parent and when the embedded data remains reasonably bounded in size. This includes small sets of child values, such as addresses, settings, or a limited list of recent items. When you embed documents, MongoDB Server can improve performance by retrieving related data in a single database operation, and by updating related data atomically within a single document.
Consider embedding data in the following cases:
Your application usually reads parent and child data together. Fetching everything in one query is more efficient than making separate calls.
The related data doesn't grow without a clear upper bound. Bounded data won't cause documents to grow indefinitely or hit MongoDB's 16MB document size limit.
The related data doesn't change often and independently of the parent. This incurs less overhead from rewriting the parent document.
Note
Nested Hierarchies
When you update any property of a nested owned entity, the EF Core Provider rewrites the entire root document. For entity hierarchies with three or more levels of nesting, this write cost compounds, because a small change deep in the tree triggers a full parent document rewrite. If your model uses deep nesting and your application performs frequent writes, consider flattening the hierarchy or replacing inner owned entities with manual references.
To learn more, see Embedded Data in the MongoDB Server documentation.
How to Embed Documents
You can designate an owned entity in the following ways:
[Owned] Attribute: Apply the
[Owned]data annotation attribute to the model class for the subdocument. This approach is declarative and concise, but also less flexible, because it applies globally to all usages of the owned type.Fluent API: Call the
OwnsOne()orOwnsMany()methods in yourDbContextconfiguration. This approach offers more control and flexibility, allowing you to configure the relationship differently for specific entities without modifying the model class.
The following sections provide examples of these approaches for modeling single and multiple owned entities.
One Owned Entity
To embed a single document within a parent document, call the OwnsOne()
method in your DbContext configuration or apply the [Owned] attribute
to the embedded class.
The following example shows a Customer entity that contains an embedded
Address document. Select the [Owned] Attribute or
Fluent API tab to see the corresponding syntax:
[] 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"); }); } }
When you save a Customer entity, MongoDB stores the Address as an embedded
document within the Customer document. The following JSON shows how this relationship
appears in MongoDB:
{ "_id": ObjectId("..."), "Name": "John Doe", "Address": { "Street": "123 Main St", "City": "New York", "Country": "USA" } }
Multiple Owned Entities
To embed multiple documents in a parent document, call the OwnsMany()
method or apply the [Owned] attribute to the embedded class and define a
collection property.
The following example shows a Customer entity that contains multiple
embedded Order documents. Select the [Owned] Attribute or
Fluent API tab to see the corresponding syntax:
[] 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"); }); } }
When you save a CustomerWithOrders entity, MongoDB stores the Orders as an array of
embedded documents, as shown in the following JSON:
{ "_id": ObjectId("..."), "Name": "Jane Smith", "Orders": [ { "Product": "Laptop", "Quantity": 1 }, { "Product": "Mouse", "Quantity": 2 } ] }
Manual References
To model relationships among entities that aren't embedded, you can store references manually by saving the ID of one document in a field of another document. This approach requires you to manage the relationship in your application code.
When to Use References
Use references when you regularly query the related data independent of its parent, when the child set can grow very large, or when the related data changes frequently.
Consider using references in the following cases:
The related data grows without a clear upper bound. Unbounded arrays can cause performance issues and might reach MongoDB's 16MB document size limit.
You need to query the related entity on its own. You can query and index the child collection directly without scanning parent documents.
The related data changes often and independently of the parent. Frequent updates to embedded data require rewriting the entire parent document each time, which is inefficient.
The application doesn't usually read parent and child data together. By using references, you avoid fetching child data that you don't need.
The relationship is many-to-many or otherwise complex.
Duplicating the data would create too much update overhead.
To learn more, see Reference Data in the MongoDB Server documentation.
How to Use References
The following example shows how to store a reference to a related entity:
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; } }
To retrieve the related entity, you must query the referenced collection separately, as shown in the following example:
// 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);
Use the Subset Pattern for Large One-to-Many Sets
If a parent document contains a large array but your application usually uses only the most recent or most frequently accessed items, consider the Subset Pattern.
In the subset pattern, you keep the most frequently accessed items in the main document and move the rest into a separate collection. This approach can reduce document size and keep more of your working set in RAM, which can improve query performance.
This pattern is useful when you want the performance benefits of embedding, but a full embedded history would cause documents to become too large or too expensive to load.
Additional Information
To learn more about best practices for schema design in MongoDB, see Best Practices for Data Modeling in MongoDB and Designing Your Schema in the MongoDB Server documentation.
To learn more about modeling data in MongoDB, see Data Modeling in the MongoDB Server documentation.
To learn more about owned entity types in Entity Framework Core, see Owned Entity Types in the Microsoft Entity Framework Core documentation.
API Documentation
To learn more about the methods and types discussed in this guide, see the following Microsoft API documentation: