-
-
Notifications
You must be signed in to change notification settings - Fork 637
Open
Description
Description
When using BulkInsertOrUpdate with IncludeGraph = true, if the first entity in the list has a null navigation property (Child == null), then the ChildId foreign key is not populated for subsequent parents — even if they have valid Child objects.
As a result:
- Child entities are inserted
- but ChildId remains null in Parent records
Sorting the list so that entities with non-null Child come first works around the issue, but it's not a proper solution.
Packages
<PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="8.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.13" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Example code
using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore;
namespace BulkInsertBugExample
{
public class Parent
{
public long Id { get; set; }
public long? ChildId { get; set; }
public Child Child { get; set; }
}
public class Child
{
public long Id { get; set; }
public string Name { get; set; }
public Parent Parent { get; set; }
}
public class TestDbContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("Server=127.0.0.1;Port=5432;Database=bulk-tests;User Id=postgres;Password=postgres;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Parent>()
.HasOne(p => p.Child)
.WithOne(ch => ch.Parent)
.HasForeignKey<Parent>(p => p.ChildId);
}
}
internal class Program
{
static void Main(string[] args)
{
CreateClearDb();
BadInsert();
GoodInsert();
Console.WriteLine();
}
private static void BadInsert()
{
using TestDbContext context = new TestDbContext();
var parents = new List<Parent>
{
new Parent { Child = null },
new Parent { Child = new Child { Name = "Child 1" } },
new Parent { Child = null },
new Parent { Child = new Child { Name = "Child 2" } },
new Parent { Child = null },
new Parent { Child = new Child { Name = "Child 3" } },
};
context.BulkInsertOrUpdate(parents, new BulkConfig
{
IncludeGraph = true
});
var parentsFromDb = context.Parents.Include(p => p.Child).ToList();
Console.WriteLine("Parents in DB:");
foreach (var parent in parentsFromDb)
{
Console.WriteLine($"Parent Id={parent.Id}, ChildId={parent.ChildId}, ChildName={parent.Child?.Name}");
}
}
private static void GoodInsert()
{
using TestDbContext context = new TestDbContext();
var parents = new List<Parent>
{
new Parent { Child = new Child { Name = "Child 4" } },
new Parent { Child = null },
new Parent { Child = new Child { Name = "Child 5" } },
new Parent { Child = null },
new Parent { Child = new Child { Name = "Child 6" } },
new Parent { Child = null },
};
context.BulkInsertOrUpdate(parents, new BulkConfig
{
IncludeGraph = true
});
var parentsFromDb = context.Parents.Include(p => p.Child).ToList();
Console.WriteLine("Parents in DB:");
foreach (var parent in parentsFromDb)
{
Console.WriteLine($"Parent Id={parent.Id}, ChildId={parent.ChildId}, ChildName={parent.Child?.Name}");
}
}
private static TestDbContext CreateClearDb()
{
using TestDbContext context = new TestDbContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
return context;
}
}
}Metadata
Metadata
Assignees
Labels
No labels