Part of DKNet.EfCore.Extensions. Walk collection navigations from possible updating roots and add discovered new children in one batch.
Use the console-project setup in the Quick Start, then replace Program.cs with this example.
using Microsoft.EntityFrameworkCore;
using DKNet.EfCore.Extensions.Extensions;
using var connection = new Microsoft.Data.Sqlite.SqliteConnection("Data Source=:memory:");
connection.Open();
var o = new DbContextOptionsBuilder<DemoDb>();
o.UseSqlite(connection);
await using var db = new DemoDb(o.Options);
await db.Database.EnsureCreatedAsync();
var parent = new Item { Name = "Coffee" };
db.Add(parent);
await db.SaveChangesAsync();
db.ChangeTracker.AutoDetectChangesEnabled = false;
parent.Name = "Coffee beans";
db.Entry(parent).Property(x => x.Name).IsModified = true;
parent.Children.Add(new Child());
Console.WriteLine(db.GetNewEntitiesFromNavigations(db.Entry(parent)).Count()); // 1
Console.WriteLine(await db.AddNewEntitiesFromNavigations()); // 1: the child is considered new
db.ChangeTracker.AutoDetectChangesEnabled = true;
await db.SaveChangesAsync();
public sealed class DemoDb(DbContextOptions<DemoDb> options) : DbContext(options)
{
public DbSet<Item> Items => Set<Item>();
}
public sealed class Item
{
public int Id { get; set; }
public string Name { get; set; } = "";
public List<Child> Children { get; set; } = [];
}
public sealed class Child
{
public int Id { get; set; }
public int ItemId { get; set; }
}
GetPossibleUpdatingEntities calls DetectChanges and selects Detached/Modified entries. GetNewEntitiesFromNavigations(entry) scans one root; its parameterless form scans those possible roots. GetCollectionNavigations(Type) selects collection, non-shadow INavigation metadata.
GetNavigationValues(object, INavigation) reads a property or backing field and returns an empty sequence when no enumerable is available. The collection walker itself uses EF entry collection access.