Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to apply naming convention to json properties? #65

Closed
Arsync opened this issue Feb 8, 2021 · 3 comments
Closed

How to apply naming convention to json properties? #65

Arsync opened this issue Feb 8, 2021 · 3 comments

Comments

@Arsync
Copy link

Arsync commented Feb 8, 2021

I have models like

public class Employee
{
    public EmployeeSettings Settings { get; set; }
}

public class EmployeeSettings
{
    public string DefaultArea { get; set; }
}

Query for data:

dbContext.Employees.FirstOrDefault(x => x.Settings.DefaultArea == "admin");

And with .UseSnakeCaseNamingConvention() it still looks like this in SQL:

AND (e.settings->>'DefaultArea' = 'admin')

but needed:

AND (e.settings->>'default_area' = 'admin')

I've tried even this (without success):

public class EmployeeMap : IEntityTypeConfiguration<Employee>
{
    public void Configure(EntityTypeBuilder<Employee> b)
    {
        var serializerSettings = new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = new DefaultContractResolver
            {
                NamingStrategy = new SnakeCaseNamingStrategy()
            }
        };

        b.Property(e => e.Settings)
            .HasColumnType("jsonb")
            .HasConversion(
                v => JsonConvert.SerializeObject(v, serializerSettings),
                v => JsonConvert.DeserializeObject<EmployeeSettings>(v, serializerSettings)
            );
    }
}
@roji
Copy link
Member

roji commented Feb 8, 2021

@Arsync properties inside JSON documents aren't covered by this plugin, at least not yet. The way JSON support is currently implemented, EF Core is completely unaware of everything happening inside a JSON document (it isn't part of the EF model), so this plugin can't rewrite any names.

The way to manage this would be via the JsonSerializerOptions of System.Text.Json, which is used to actually serialize/deserialize JSON documents. Setting in the PG EF provider JsonSerializerOptions isn't currently supported out of the box either (npgsql/efcore.pg#1107), but see this workaround which should provide a solution.

Am going to close this for now, but will reopen if JSON support changes in a way that makes it relevant.

@roji roji closed this as completed Feb 8, 2021
@Arsync
Copy link
Author

Arsync commented Feb 9, 2021

Tried to override mapping for NpgsqlDbType.Jsonb with JsonSerializerOptions naming policy changed to SnakeCaseNamingPolicy, but select and where queries still looks like e.settings->>'DefaultArea'. Perhaps, I've missed something?

Following code not worked:

private static void OverrideJsonType(NpgsqlDbType type)
{
    var origJsonbMapping =
        NpgsqlConnection.GlobalTypeMapper.Mappings.Single(m => m.NpgsqlDbType == type);

    NpgsqlConnection.GlobalTypeMapper.RemoveMapping(origJsonbMapping.PgTypeName);
    NpgsqlConnection.GlobalTypeMapper.AddMapping(new NpgsqlTypeMappingBuilder
    {
        PgTypeName = origJsonbMapping.PgTypeName,
        NpgsqlDbType = origJsonbMapping.NpgsqlDbType,
        DbTypes = origJsonbMapping.DbTypes,
        ClrTypes = origJsonbMapping.ClrTypes,
        InferredDbType = origJsonbMapping.InferredDbType,
        TypeHandlerFactory = new JsonbHandlerFactory(new JsonSerializerOptions
        {
            PropertyNamingPolicy = new SnakeCaseNamingPolicy()
        })
    }.Build());
}

Used as:

b.UseNpgsql(GetDefaultConnectionString(), opts =>
    {
        opts.UseNodaTime();

        OverrideJsonType(NpgsqlDbType.Json);
        OverrideJsonType(NpgsqlDbType.Jsonb);
        OverrideJsonType(NpgsqlDbType.JsonPath);
    })
    .UseSnakeCaseNamingConvention();

Still got queries like

SELECT e.settings->>'DefaultArea' AS "DefaultArea"
...

@roji
Copy link
Member

roji commented Feb 10, 2021

You're right - I didn't mention that the JsonSerializerOptions apply when System.Text.Json is loading or saving the JSON document to the database, but not when constructing queries that drill into it. For this scenario, the only option you currently have is to specify [JsonPropertyName] on your properties - the EF provider does respect that. It's not a good solution, but it's the only onle until npgsql/efcore.pg#1107 is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants