Custom type with automatic serialization/deserialization in EF Core
Storing a complex entity as JSON in a single database column turns out to be pretty easy with the Value Conversions which were added in EF Core 2.1.
[NotMapped] not needed
public class AvailabilityRule: ApplicationEntity
{
...
// [NotMapped]
public CalendarEvent Event { get; set; }
}
Add the following extension for PropertyBuilder:
public static class PropertyBuilderExtensions
{
public static PropertyBuilder<T> HasJsonConversion<T>(this PropertyBuilder<T> propertyBuilder) where T : class, new()
{
ValueConverter<T, string> converter = new ValueConverter<T, string>
(
v => JsonSerializer.Serialize(v, null),
v => JsonSerializer.Deserialize<T>(v, null) ?? new T()
);
ValueComparer<T> comparer = new ValueComparer<T>
(
(l, r) => JsonSerializer.Serialize(l, null) == JsonSerializer.Serialize(r, null),
v => v == null ? 0 : JsonSerializer.Serialize(v, null).GetHashCode(),
v => JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(v, null), null)
);
propertyBuilder.HasConversion(converter);
propertyBuilder.Metadata.SetValueConverter(converter);
propertyBuilder.Metadata.SetValueComparer(comparer);
propertyBuilder.HasColumnType("jsonb");
return propertyBuilder;
}
}
In the OnModelCreating method of the database context need call HasJsonConversion, which does the serialization, deserialization and track changes to your object:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<AvailabilityRule>()
.Property(b => b.Event )
.HasJsonConversion();
}