You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An aggregate root has a 1:0..1 optional dependent with some required properties, using table splitting. This dependent entity has a 1:1 required dependent where all properties are nullable (that's why this nav is required). After the optional dependent has saved values, changes on the required dependent entity are not saved.
Steps to reproduce
Build & run.
Observe the last issued SQL command and "State before save:" line on the console output.
If the required dependent is replaced: "State before save: Added" and an update command.
If the required dependent is reused: "State before save: Detached" and no update command.
(note that both City and Street columns are updated in the former case despite the Street not changing)
usingMicrosoft.EntityFrameworkCore;usingMicrosoft.Extensions.Logging;// Prep the db & seedusing(vardb=newAppDb()){db.Database.EnsureDeleted();db.Database.EnsureCreated();db.Parents.Add(new(){Id="x",Child=new(){Name="asd"}});db.SaveChanges();}// reproduce the issueusing(vardb=newAppDb()){varparent=db.Parents.Single();// This will update//parent.Child!.Address = new() { City = "asd" };// This will notparent.Child!.Address.City="asd";db.ChangeTracker.DetectChanges();Console.WriteLine($"State before save: {db.Entry(parent.Child.Address).State}");db.SaveChanges();}// ModelclassAppDb:DbContext{protectedoverridevoidOnConfiguring(DbContextOptionsBuilderbuilder){builder.UseSqlServer("Data Source=localhost;Initial Catalog=Test;Integrated Security=true").LogTo(Console.WriteLine,LogLevel.Information).EnableSensitiveDataLogging();}protectedoverridevoidOnModelCreating(ModelBuilderbuilder){builder.Entity<Parent>().OwnsOne(e =>e.Child).OwnsOne(e =>e.Address);}publicDbSet<Parent>Parents=>this.Set<Parent>();}classParent{publicrequiredstringId{get;set;}publicChild?Child{get;set;}}classChild{publicrequiredstringName{get;set;}publicAddressAddress{get;set;}=new();}classAddress{// All optional, hence the navigation to this entity is requiredpublicstring?City{get;set;}publicstring?Street{get;set;}}
First I thought this has to do with owned entities and thus part of #24581 and #1985 however it repros with plain old navigations. Replace OnModelCreating with the following (basically maps to the same table structure), steps and outcome is the same
the navigation is required but warns OptionalDependentWithAllNullPropertiesWarning[20704] and neither the Address nor the City and Street setters are hit on materialization
the Address is Detached on materialization, it should be Unchanged
even though Detached the change tracker still maintains some knowledge of the Address or else the replace/reuse behavior could not be possibly different. It's just an unknown object.
@bachratyg Sorry for taking a long time to respond. I think this is a bug, but if you explicitly configure both owning navigations as required, then Address is populated when the query is executed. For example:
builder.Entity<Parent>(b =>{b.OwnsOne(e =>e.Child, b =>{b.OwnsOne(e =>e.Address);b.Navigation(e =>e.Address).IsRequired();});b.Navigation(e =>e.Child).IsRequired();});
Note for team: I think this is a bug, because is Child is optional, then even if Address is marked as required (which it isn't in the original code, but I tried this) and the Child exists in the database, it's Address will not be loaded.
Marking the Child nav as required is not viable. The following would no longer work:
...
db.Database.EnsureCreated();
db.Parents.Add(new() { Id = "x", Child = new() { Name = "asd" } });
db.Parents.Add(new() { Id = "y" });
db.SaveChanges();
...
Alternatively if I make the child nav non-nullable and all properties nullable (just like Address) that would wreak havoc on the business logic side of things.
An aggregate root has a 1:0..1 optional dependent with some required properties, using table splitting. This dependent entity has a 1:1 required dependent where all properties are nullable (that's why this nav is required). After the optional dependent has saved values, changes on the required dependent entity are not saved.
Steps to reproduce
Build & run.
Observe the last issued SQL command and "State before save:" line on the console output.
If the required dependent is replaced: "State before save: Added" and an update command.
If the required dependent is reused: "State before save: Detached" and no update command.
(note that both City and Street columns are updated in the former case despite the Street not changing)
First I thought this has to do with owned entities and thus part of #24581 and #1985 however it repros with plain old navigations. Replace OnModelCreating with the following (basically maps to the same table structure), steps and outcome is the same
Some additional observations:
Include provider and version information
EF Core version: 8.0.8
Database provider: Microsoft.EntityFrameworkCore.SqlServer (probably others)
Target framework: .NET 8.0
The text was updated successfully, but these errors were encountered: