Skip to content

JSON Columns ​

Starting with Weasel 8.11.1, the EF Core integration supports JSON column mappings defined via OwnsOne().ToJson(). This was previously silently ignored, causing JSON columns to be missing from Weasel's schema model (GitHub issue #232).

How It Works ​

When EF Core maps a complex property to a JSON column using ToJson(), Weasel detects this during table mapping by iterating each entity type's navigations. For each navigation whose target entity type returns true from IsMappedToJson(), Weasel:

  1. Reads the column name via GetContainerColumnName().
  2. Reads the column type via GetContainerColumnType(), falling back to the provider's own JSON store type when the model does not name one -- jsonb on PostgreSQL, nvarchar(max) on SQL Server, TEXT on MySQL and SQLite, CLOB on Oracle (Migrator.DefaultJsonColumnType). The fallback used to be the literal jsonb on every provider, which produced invalid DDL anywhere but PostgreSQL (weasel#628).
  3. Sets nullability based on whether the navigation's foreign key is marked as required.

Complex Properties Without ToJson() ​

A ComplexProperty that is not ToJson() is table-split: its members become ordinary columns of the owner's table, not a JSON container. Those are mapped too -- see Table Mapping. The two shapes can coexist on one entity.

EF Core Configuration ​

Define a JSON column using ComplexProperty with ToJson():

cs
public class OrderDbContext : DbContext
{
    public OrderDbContext(DbContextOptions<OrderDbContext> options) : base(options)
    {
    }

    public DbSet<Order> Orders { get; set; } = null!;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>(entity =>
        {
            entity.ToTable("orders", "myschema");
            entity.HasKey(x => x.Id);
            entity.Property(x => x.Id).HasColumnName("id");
            entity.Property(x => x.Status).HasColumnName("status");
            entity.OwnsOne(x => x.ShippingAddress, b =>
            {
                b.ToJson("shipping_address");
            });
        });
    }
}

snippet source | anchor

The resulting Weasel table will include three columns:

  • id -- primary key
  • status -- text column
  • shipping_address -- jsonb column, non-nullable (because IsRequired() was called)

Column Type ​

The column type defaults to jsonb for PostgreSQL. If EF Core specifies a different container column type via its metadata, that type is used instead. For SQL Server, this would typically be nvarchar(max).

Nullability ​

The JSON column's nullability is determined by the navigation's foreign key IsRequired setting:

  • b.IsRequired() -- column is NOT NULL
  • No IsRequired() call -- column allows NULL

Owned Types Exclusion ​

Owned entity types (those configured via OwnsOne() or OwnsMany()) are excluded from GetEntityTypesForMigration() since they do not produce their own tables. This fix was added in Weasel 8.11.2 to address GitHub issue #234, which caused errors when owned types were incorrectly treated as standalone tables.

Migration Example ​

cs
await using var migration = await serviceProvider.CreateMigrationAsync(dbContext, ct);

// The migration will include the JSON column in the table definition.
// If the column was previously missing (pre-8.11.1), the delta detection
// will flag it as a new column to add.
if (migration.Migration.Difference != SchemaPatchDifference.None)
{
    await migration.ExecuteAsync(AutoCreate.CreateOrUpdate, ct);
}

snippet source | anchor

Released under the MIT License.