I'm using EF Core 8, with the Postgres adapter.
I need this entity property as JSON:
public ICollection<string> Messages { get; } = [];
I tried this configuration:
builder.OwnsOne(x => x.Messages, x => x.ToJson());
That didn't work as it saved "{ Capacity: 4 }"
in the database.
I tried this configuration:
builder.Property(x => x.Messages).HasColumnType("jsonb");
That throws a NotSupportedException
.
I tried various other combinations and workarounds.
For years now, although JSON mapping has been improved, there are always bugs or shortcomings.
I know that .NET 9 was released yesterday, possibly with some improvements for collections of primitives and strings, but we can't upgrade now.
What works in .NET 8?
I found the answer in the docs for value converters. It serialises to json despite not using HasColumnType("jsonb")
or ToJson()
.
modelBuilder
.Entity<Customer>()
.Property(x => x.Messages)
//.HasColumnType("jsonb") // unnecessary
.HasConversion(
c => JsonSerializer.Serialize(c, JsonSerializerOptions.Default),
s => JsonSerializer.Deserialize<ICollection<string>>(s, JsonSerializerOptions.Default)!,
new ValueComparer<ICollection<string>>(
(c1, c2) => c1!.SequenceEqual(c2!),
c => c.Aggregate(0, (acc, val) => HashCode.Combine(acc, val.GetHashCode(StringComparison.Ordinal))),
c => new Collection<string>(c.ToList()))
);
Example:
customer.Messages.Add("foo");
customer.Messages.Add("bar");
customer.Messages.Add("baz");
The db record will have:
["foo","bar","baz"]