ℹ️ Graph inserts that require database-generated key propagation are not supported for Oracle and MySQL providers due to limitations in retrieving generated IDs. Graph inserts using client-generated keys (e.g., GUIDs with
ValueGeneratedNever()) are supported on all providers.
This library supports bulk inserting entire object graphs, including entities with their related navigation properties.
await dbContext.ExecuteBulkInsertAsync(blogs, options =>
{
options.IncludeGraph = true;
});- The library traverses all reachable entities via navigation properties
- Entities are sorted in topological order (parents before children) to respect foreign key constraints
- Each entity type is bulk inserted in dependency order
- Generated IDs (identity columns) are propagated to foreign key properties
- Many-to-many join tables with explicit join entity types are populated automatically (see Limitations below)
| Option | Default | Description |
|---|---|---|
IncludeGraph |
false |
Enable graph traversal |
MaxGraphDepth |
0 (unlimited) |
Maximum depth to traverse. Use 0 for unlimited. |
IncludeNavigations |
null (all) |
Specific navigation property names to include |
ExcludeNavigations |
null (none) |
Navigation property names to exclude |
- ✅ One-to-Many (e.g., Blog → Posts)
- ✅ Many-to-One (e.g., Post → Blog)
- ✅ One-to-One (e.g., Blog → BlogSettings)
- ✅ Many-to-Many with join table (e.g., Post ↔ Tags)
- ✅ Self-referencing/Hierarchies (e.g., Category → Parent/Children)
- Graph insert is inherently slower than flat insert due to FK propagation overhead
- For entities with identity columns, the library uses
ExecuteBulkInsertReturnEntitiesAsyncinternally to retrieve generated IDs - Consider using client-generated keys (GUIDs with
ValueGeneratedNever()) to avoid ID propagation overhead - Use
MaxGraphDepthto limit traversal for large/deep graphs - Use
IncludeNavigationsorExcludeNavigationsto reduce the scope of insertions
var blog = new Blog
{
Name = "My Blog",
Posts = new List<Post>
{
new Post { Title = "First Post" },
new Post { Title = "Second Post" }
}
};
await dbContext.ExecuteBulkInsertAsync(new[] { blog }, o => o.IncludeGraph = true);
// After insert:
// - blog.Id is populated
// - blog.Posts[0].BlogId == blog.Id
// - blog.Posts[1].BlogId == blog.Idvar blog = new Blog
{
Name = "My Blog",
Settings = new BlogSettings { EnableComments = true }
};
await dbContext.ExecuteBulkInsertAsync(new[] { blog }, o => o.IncludeGraph = true);
// After insert:
// - blog.Id is populated
// - blog.Settings.BlogId == blog.Idvar blog = new Blog
{
Name = "My Blog",
Posts = new List<Post> { new Post { Title = "Post" } },
Settings = new BlogSettings { EnableComments = true }
};
// Only insert Posts, not Settings
await dbContext.ExecuteBulkInsertAsync(new[] { blog }, o =>
{
o.IncludeGraph = true;
o.IncludeNavigations = new HashSet<string> { "Posts" };
});var blog = new Blog
{
Name = "My Blog",
Posts = new List<Post>
{
new Post
{
Title = "Post",
Tags = new List<Tag> { new Tag { Name = "EF Core" } } // Won't be inserted
}
}
};
// MaxGraphDepth = 1 means only Blog and direct children (Posts)
await dbContext.ExecuteBulkInsertAsync(new[] { blog }, o =>
{
o.IncludeGraph = true;
o.MaxGraphDepth = 1;
});- Shadow foreign keys: Currently not supported. Add a CLR property for foreign keys.
- Circular references: Handled gracefully by tracking visited entities, but may result in incomplete graphs.
- Owned entities: Owned entity types are not included in graph traversal and are not inserted when using
IncludeGraph = true. - Self-referencing hierarchies: Multi-level self-referencing hierarchies (e.g., Category → Children) require multiple insert operations. Root entities can be inserted, but nested children with FK references to other entities of the same type within the same batch are not supported.
- Many-to-many join tables: Entities on both sides of many-to-many relationships are traversed and inserted. However, automatic join table population only works with explicit join entity types (not
Dictionary<string, object>shared-type entities). - OnConflict/Upsert: Not currently supported with
IncludeGraph = true.