npgsql / efcore.pg

Entity Framework Core provider for PostgreSQL
PostgreSQL License
1.54k stars 226 forks source link

HasPostgresArrayConversion cannot map an array nested inside value object #2082

Open samisq opened 2 years ago

samisq commented 2 years ago

HasPostgresArrayConversion cannot map an array that's nested inside a simple value object, because it expects the array property to be accessed directly from the entity.

For example, consider the model below:

public class TestEntity
{
    public TestEntity(Guid id, IntSequence sequence)
    {
        Id = id;
        Sequence = sequence;
    }
    public Guid Id { get; }
    public IntSequence Sequence { get; }
}
public class IntWrapper
{
    public int Value { get; }
    public IntWrapper(int value) => Value = value;
}
public class IntSequence
{
    public IntWrapper[] Values { get; }
    public IntSequence(IntWrapper[] values) => Values = values;
}

Previously, we could accomplish this with HasConversion, like:

modelBuilder.Entity<TestEntity>()
            .Property(x => x.Sequence)
            .HasConversion(x => x.Values.Select(v => v.Value).ToArray(),
                v => new IntSequence(v.Select(w => new IntWrapper(w)).ToArray()));

In domain-driven design, it's common to encapsulate primitives into value objects to create rich domain model that handles domain validation, and other business rules. Is there a recommended way to address this in v6?

Blackclaws commented 1 year ago

@samisq ValueObjects should use the OwnsOne, OwnsMany, or direct conversion to a literal pattern.

In your case it should be:

        modelBuilder.Entity<TestEntity>().OwnsOne(x => x.Sequence, builder =>
        {
            builder.Property(x => x.Values)
                .HasPostgresArrayConversion<IntWrapper, int>(x => x.Value, i => new IntWrapper(i));
        });