-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Differnce in behavior between TPH and TPT/TPC with new entity added through navigation property #32084
Comments
@Euphoric I am not able to reproduce this--my code is below. However, it's likely because you are assigning explicit values to Guid keys even though they are configured by default to be auto-generated. Consider either not providing explicit values, or configuring the key properties as not-generated--for example, using (var context = new SomeDbContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
var parentId = Guid.NewGuid();
context.Add(new ParentEntity {Id = parentId});
var parent = await context.Parents.FindAsync(parentId);
var child = new ChildEntity {Id = Guid.NewGuid(), ParentId = parent!.Id, ChildValue = "test value"};
//await context.Set<ChildBaseEntity>().AddAsync(child); // fixes
parent.Child = child;
await context.SaveChangesAsync();
}
public class SomeDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=AllTogetherNow")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();
public DbSet<ParentEntity> Parents => Set<ParentEntity>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var parent = modelBuilder.Entity<ParentEntity>();
parent.HasOne(x => x.Child)
.WithOne()
.HasForeignKey<ChildBaseEntity>(x=>x.ParentId)
;
var childBase = modelBuilder.Entity<ChildBaseEntity>();
//childBase.UseTphMappingStrategy(); // Case 1 : Works correctly
//childBase.UseTptMappingStrategy(); // Case 2 : Doesn't work
childBase.UseTpcMappingStrategy(); // Case 3 : Doesn't work
var child = modelBuilder.Entity<ChildEntity>();
child.HasBaseType<ChildBaseEntity>(); }
}
public class ParentEntity
{
public Guid Id { get; set; }
public ChildBaseEntity? Child { get; set; }
}
public abstract class ChildBaseEntity
{
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
public class ChildEntity : ChildBaseEntity
{
public String? ChildValue { get; set; }
} |
@ajcvickers Can you try storing the parent in one dbContext scope and loading it+assigning child in second scope? |
@Euphoric In that case it should fail for all inheritance strategies. |
@ajcvickers So. If I'm understanding it correctly. Correct lifetime tracking for entities added as part of navigation property is supported if no inheritance is involved. But is unsuported or undefined if the entity is part part of inheritance tree? |
@Euphoric No, it should not be affected by the mapping strategy. That's why we need a repro. |
@ajcvickers I'm sorry, I'm confused here. Above, I said that to reproduce the issue, the insert of new parent and assignment of child must happen in separate DbContext scopes. I can reproduce it when i use:
My assumptions are :
Then, the question is how does inheritance strategies involve into this. Either it is expected to work for all of the inheritance strategies, in which case TPT and TPC are buggy. Or it is not expected to work and TPH working is just a incidental to work. |
@Euphoric Thanks for the repro code; looks like this is a bug that happens when generated key values have been explicitly set on an entity mapped via TPH. The correct thing to do is, as stated above, to configure your keys as not-generated, or to stop setting explicit values and let EF generate them. Note for triage: bug was introduced by the fix for #26448. The dependent gets marked as Added because the discriminator value generator generates a stable value. But the presence of a non-stable generator should stop this from happening even if that generator was not used because the value has been explicitly set. |
@ajcvickers Thank you for verifying. I can confirm that setting up |
File a bug
I'm having an issue where TPH works but TPT/TPC does not. When I'm creating a new entity and adding it by assigning it to navigation property.
Include your code
Full code to reproduce : https://github.com/Euphoric/EfCore.InheritanceIssue/blob/main/EfCore.InheritanceIssue/EfCore.InheritanceIssue/Program.cs
The relevant pieces are :
When using TPH, this code works as expected.
But when using TPT or TPC this code fails when trying to save changes:
Looking at log, the difference is that for TPH, EF generates INSERT:
But for TPC, EF generates UPDATE, even though the row was never inserted before:
I have also verified this is issue on both PosgreSql and SqlServer.
Workaround
Adding the child entity explicity fixes the issue and makes EF insert it as new entity.
Question
Is this expected behavior?
If yes, is it possible to configure EF so it maps to table-per-concretetype and works correctly even without adding the new referenced entity explicitly?
Include provider and version information
EF Core version: 7.0.12
Database provider: PostgreSql, SqlServer
Target framework: .NET 7
Operating system: Windows 10
IDE: Rider 2023.2.2
The text was updated successfully, but these errors were encountered: