Skip to content

Settings.AddJsonColumnMappings

Simon Hughes edited this page Aug 30, 2026 · 1 revision

Settings.AddJsonColumnMappings

Maps a JSON column to a real C# class instead of leaving it as a string.

Type Action<List<JsonColumnMapping>>
Default Adds nothing
Applies to EF Core mainly; ToJson() needs EF Core 7+
Databases SQL Server (json, nvarchar), PostgreSQL (json, jsonb), MySQL (json)
In Database.tt? Yes, with commented-out examples

What it does

A JSON column arrives as a string, because that is what it is at the database level. That is honest and useless - you end up deserialising it by hand at every call site.

This setting tells the generator that a particular column holds a particular shape, so the property is typed as that class:

Settings.AddJsonColumnMappings = delegate(List<JsonColumnMapping> jsonColumnMappings)
{
    jsonColumnMappings.Add(new JsonColumnMapping
    {
        Schema              = "dbo",
        Table               = "Orders",
        Column              = "ShippingAddress",
        PropertyType        = "Address",
        AdditionalNamespace = "MyApp.Models"
    });

    // Wildcards work on schema and table
    jsonColumnMappings.Add(new JsonColumnMapping
    {
        Schema       = "*",
        Table        = "*",
        Column       = "Metadata",
        PropertyType = "Dictionary<string, object>"
    });
};

JsonColumnMapping has six fields:

Field Purpose
Schema Schema name, or "*"
Table Table name, or "*"
Column The column to map
PropertyType The C# type, written verbatim
AdditionalNamespace A using to add for that type
ExcludePropertyConfiguration Suppresses the generated builder.Property(...) line - see below

ExcludePropertyConfiguration

Set it true when you intend to configure the property yourself with OwnsOne(...).ToJson(), EF Core 7's way of mapping a JSON column to an owned type that can be queried into.

The generator's own builder.Property(...) line and your OwnsOne block cannot both configure the same property, so this is how you turn the first one off:

jsonColumnMappings.Add(new JsonColumnMapping
{
    Schema                       = "dbo",
    Table                        = "Orders",
    Column                       = "ShippingAddress",
    PropertyType                 = "Address",
    ExcludePropertyConfiguration = true    // I will do the OwnsOne(...).ToJson() myself
});

When to use it

Any JSON column with a stable shape. If the same keys are always there, a type is better than a string.

Dictionary<string, object> for genuinely free-form JSON, which at least saves the deserialisation call.

Leave it alone for JSON you never read in C# - an audit blob, a payload passed through to somewhere else. A string is the right type for something you do not look inside.

Gotchas

You still need a converter, unless you use ToJson(). Typing the property as Address does not tell EF how to turn the column's text into one. Either supply a value converter in a partial OnModelCreatingPartial, or use the OwnsOne(...).ToJson() route with ExcludePropertyConfiguration = true. Without one of the two, the model will not build at run time - and the generated code compiles perfectly, so you find out late.

PropertyType is written verbatim and not validated. The class must exist, and you must write it - the generator does not produce it. This is the opposite of Settings.AddOwnedEntityMappings, which does generate the class.

AdditionalNamespace is per mapping, unlike the global Settings.AdditionalNamespaces. Both work; the per-mapping one keeps the using off files that do not need it.

Wildcards match broadly. Schema = "*", Table = "*", Column = "Metadata" catches every Metadata column in the database, including ones that are not JSON.

ToJson() needs EF Core 7 or later and has real query limitations - you cannot index into a JSON collection in a WHERE clause on every provider.

It relies on Settings.ApplyJsonColumnMappings being called from Settings.UpdateColumn, which the shipped Database.tt does. Replace UpdateColumn and drop that call, and this setting silently stops working.

See also

Clone this wiki locally