For legacy reason we use an injected lazy loader in EF Core.
In the code below Context.Add(child) results in the lazy loaded navigation collection being updated but Context.Remove(child) does not. SaveChanges() must be called first. Is this by design?
Now I know I can call Parent.Children.Remove(child) and that is hunky dory but our solution requires invoking Context.Remove(child). I don't like just chucking in SaveChanges() without understanding why it is necessary.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace EFDemo
{
public class Parent
{
private Action<object, string> _lazyLoader;
public Parent() { }
public Parent(Action<object, string> lazyLoader)
{
_lazyLoader = lazyLoader;
}
public int Id { get; set; }
public string Name { get; set; }
private List<Child> _children;
public List<Child> Children {
get
{
_lazyLoader?.Invoke(this, "Children");
_children ??= new List<Child>();
return _children;
}
set => _children = value;
}
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
}
public class EFDemoContext : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Children { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString =
"Server=tcp:localhost,1433;Initial Catalog=EFDemo;Persist Security Info=False;User ID=xxxxx;Password=xxx@xxxx;Encrypt=False;Max Pool Size=500;Pooling=True;";
optionsBuilder.UseSqlServer(connectionString)
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableDetailedErrors()
.EnableSensitiveDataLogging();
base.OnConfiguring(optionsBuilder);
}
}
class Program
{
static void Main(string[] args)
{
var context = new EFDemoContext();
context.Database.EnsureDeleted();
context.Database.Migrate();
var p = new Parent()
{
Name = "Alice",
Children = new List<Child>
{
new() { Name = "Bob" },
new() { Name = "Charlie" },
new() { Name = "Doris" },
}
};
context.Add(p);
context.SaveChanges();
Debug.Assert(p.Children.Count == 3, "Initial children not found");
context.Add(new Child() { Name = "Elizabeth", Parent = p });
Debug.Assert(p.Children.Count == 4, "New child not found");
context.Remove(p.Children.First());
//context.SaveChanges(); // we need to save in order to succeed
Debug.Assert(p.Children.Count == 3, "Child not removed");
}
}
}
--- Followup --
This issue addresses the matter precisely. There is some inconsistency I think in how fix up occurs for deletes but clearly an internal EFCore fix for this broke too many things.