-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Closed as not planned
Closed as not planned
Copy link
Labels
Description
Steps to reproduce the issue:
- Define a owned entity with only a String property.
- Define a owned entity with only a Guid property.
- Define a owner entity with two lists of the two owned entities defined before.
- Map both owned entities to a table with OwnsMany in the owner entity.
- For both owned entities don't add a surrogate key but define the key as the combination of the foreign key and their only property.
- Insert a owner entity with values in both owned entities: when inserting, EF correctly generates insert statement for the owned entities
- Update the owner entity created before changing the values of the owned entities: when updating, EF generates the insert statement only for the owned entity with a String property and not for the owned entity with a Guid property (however it deletes the owned entities as it recognizes they are changed).
See attached project or the console application code below.
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=testPassword1!" -p 1433:1433 --name test-db --hostname test-db -d mcr.microsoft.com/mssql/server:2022-latest
using Microsoft.EntityFrameworkCore;
var id = Guid.NewGuid();
using (var context = new TestDbContext())
{
var owners = await context.OwnerEntities.ToListAsync();
foreach (var owner in owners)
{
context.OwnerEntities.Remove(owner);
context.SaveChanges();
}
}
using (var context = new TestDbContext())
{
var owner = new OwnerEntity(id);
owner.SetOwnedEntitiesWithString(new OwnedEntityWithString("Hello"), new OwnedEntityWithString("World"));
owner.SetOwnedEntitiesWithGuid(new OwnedEntityWithGuid(Guid.NewGuid()), new OwnedEntityWithGuid(Guid.NewGuid()));
context.OwnerEntities.Add(owner);
context.SaveChanges();
}
using (var context = new TestDbContext())
{
var owner = await context.OwnerEntities.FindAsync(id) ?? throw new Exception();
Console.WriteLine($"OwnedEntitiesWithString after insert: {owner.OwnedEntitiesWithString.Count}"); //Should be 2. Is 2.
Console.WriteLine($"OwnedEntitiesWithGuid after insert: {owner.OwnedEntitiesWithGuid.Count}"); //Should be 2. Is 2.
owner.SetOwnedEntitiesWithString(new OwnedEntityWithString("Ciao"), new OwnedEntityWithString("Mondo"));
owner.SetOwnedEntitiesWithGuid(new OwnedEntityWithGuid(Guid.NewGuid()), new OwnedEntityWithGuid(Guid.NewGuid()));
context.SaveChanges();
}
using (var context = new TestDbContext())
{
var owner = await context.OwnerEntities.FindAsync(id) ?? throw new Exception();
Console.WriteLine($"OwnedEntitiesWithString after update: {owner.OwnedEntitiesWithString.Count}"); //Should be 2. Is 2.
Console.WriteLine($"OwnedEntitiesWithGuid after update: {owner.OwnedEntitiesWithGuid.Count}"); //Should be 2. Is 0.
}
public class OwnerEntity
{
private readonly List<OwnedEntityWithString> ownedEntitiesWithString = new();
private readonly List<OwnedEntityWithGuid> ownedEntitiesWithGuid = new();
public Guid Id { get; private set; }
public OwnerEntity(Guid id)
{
Id = id;
}
public IReadOnlyList<OwnedEntityWithString> OwnedEntitiesWithString => ownedEntitiesWithString.AsReadOnly();
public IReadOnlyList<OwnedEntityWithGuid> OwnedEntitiesWithGuid => ownedEntitiesWithGuid.AsReadOnly();
public void SetOwnedEntitiesWithString(params OwnedEntityWithString[] ownedEntities)
{
ownedEntitiesWithString.Clear();
ownedEntitiesWithString.AddRange(ownedEntities);
}
public void SetOwnedEntitiesWithGuid(params OwnedEntityWithGuid[] ownedEntities)
{
ownedEntitiesWithGuid.Clear();
ownedEntitiesWithGuid.AddRange(ownedEntities);
}
}
public class OwnedEntityWithString
{
public string Value { get; private set; } = string.Empty;
public OwnedEntityWithString(string value)
{
Value = value;
}
}
public class OwnedEntityWithGuid
{
public Guid Value { get; private set; }
public OwnedEntityWithGuid(Guid value)
{
Value = value;
}
}
public class TestDbContext : DbContext
{
public DbSet<OwnerEntity> OwnerEntities => Set<OwnerEntity>();
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(
"Server=localhost;Database=test-db;User Id=sa;Password=testPassword1!;TrustServerCertificate=true;MultipleActiveResultSets=true");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<OwnerEntity>()
.ToTable("OwnerEntities")
.Property(x => x.Id)
.ValueGeneratedNever();
modelBuilder.Entity<OwnerEntity>()
.HasKey(x => x.Id);
modelBuilder.Entity<OwnerEntity>()
.OwnsMany(x => x.OwnedEntitiesWithString, o =>
{
o.ToTable("OwnedEntitiesWithString");
o.WithOwner()
.HasForeignKey("OwnerEntityId");
o.HasKey("OwnerEntityId", nameof(OwnedEntityWithString.Value));
o.Property(x => x.Value)
.HasMaxLength(50);
})
.OwnsMany(x => x.OwnedEntitiesWithGuid, o =>
{
o.ToTable("OwnedEntitiesWithGuid");
o.WithOwner()
.HasForeignKey("OwnerEntityId");
o.HasKey("OwnerEntityId", nameof(OwnedEntityWithGuid.Value));
});
modelBuilder.Entity<OwnerEntity>()
.Metadata
.FindNavigation(nameof(OwnerEntity.OwnedEntitiesWithString))!
.SetPropertyAccessMode(PropertyAccessMode.Field);
modelBuilder.Entity<OwnerEntity>()
.Metadata
.FindNavigation(nameof(OwnerEntity.OwnedEntitiesWithGuid))!
.SetPropertyAccessMode(PropertyAccessMode.Field);
}
}
EF Core version: 7.0.4
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (e.g. .NET 7.0)
Operating system: Windows 10 22H2
IDE: (e.g. Visual Studio 2022 17.5.2)