efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Table splitting + ConcurrencyToken throws unexpected DBConcurrencyException

Open Westboldyi opened this issue 1 year ago • 2 comments

When configuring multiple entities with table splitting and row version concurrency tokens, updating the dependents without the principal entity will throw an unexpected DbUpdateConcurrencyException.

If the principal entity (Foo) is tracked via ChangeTracker, then the updates persist properly. This occurs regardless of whether a transaction is used or not.

EF Configs:

//Principal
public class Foo
{
    public string FooProp { get; set; }
    public string FooId { get; set; } = Guid.NewGuid().ToString();

    public Bar Bar { get; set; }
    public Baz Baz { get; set; }
    private class FooConfig : IEntityTypeConfiguration<Foo>
    {
        public void Configure(EntityTypeBuilder<Foo> builder)
        {
            builder.HasKey(p => p.FooId);

            builder.HasOne(p => p.Bar).WithOne().HasForeignKey<Bar>(p => p.FooId);
            builder.HasOne(p => p.Baz).WithOne().HasForeignKey<Baz>(p => p.FooId);

            builder.Property<uint>("xmin").IsRowVersion();
            builder.ToTable("Table");
        }
    }

}

//Dependent 1
public class Baz
{
    public string Property1 { get; set; } = "prop1";
    public string FooId { get; set; } = Guid.NewGuid().ToString();

    private class BazConfig : IEntityTypeConfiguration<Baz>
    {
        public void Configure(EntityTypeBuilder<Baz> builder)
        {
            builder.HasKey(p => p.FooId);

            builder.Property<uint>("xmin").IsRowVersion();
            builder.ToTable("Table");
        }
    }

}

//Dependent 2
public class Bar
{
    public string Property2 { get; set; } = "prop2";

    public string FooId { get; set; } = Guid.NewGuid().ToString();

    private class BarConfig : IEntityTypeConfiguration<Bar>
    {
        public void Configure(EntityTypeBuilder<Bar> builder)
        {
            builder.HasKey(p => p.FooId);
                        
            builder.Property<uint>("xmin").IsRowVersion();
            builder.ToTable("Table");


        }
    }

}

image

Include provider and version information

EF Core version: Database provider: Postgresql Target framework: .NET 8 Operating system: WIN 10 IDE:VS 2022

Westboldyi avatar May 18 '24 03:05 Westboldyi

@Westboldyi can you please submit a minimal, runnable console program that shows the error happening? It's hard to piece together exactly what you're doing from a screenshot and partial snippets.

roji avatar May 18 '24 08:05 roji

Reproduction using postgres 14: https://github.com/Westboldyi/XminOrderByRepro/tree/table-splitting-db-concurrency

Westboldyi avatar May 19 '24 04:05 Westboldyi

I believe this is by-design. @AndriySvyryd can you confirm that we require the principal to be tracked for this case?

ajcvickers avatar Jun 04 '24 15:06 ajcvickers

No, this is a bug. We need to either collapse both updates into one command (by indexing using key values instead of entries) or to separate the two commands in different batches.

AndriySvyryd avatar Jun 04 '24 20:06 AndriySvyryd