EF Core 8 : JSON owned column : Update column name to camelCase

43 Views Asked by At

Context

My application is using a PostgreSQL database with a specific naming convention in camelCase.

Using EF Core 8.0.1, I'm trying to use a Jsonb column with a different naming convention in C# (PascalCase)

Each other table names, properties, primary keys and foreign keys are updated on a specific OnModelCreating function which converts each value to camelCase.

Example

I have an OrderEntity which is currently composed by several properties :

[Index(nameof(Reference), IsUnique = true)]
public class OrderEntity : BaseEntity
{
    public Guid Id { get; set; }

    public required string Reference { get; set; }

    public Guid OrganizationId { get; set; }

    //Other properties here...

    public required OrderAddress ShippingAddress { get; set; }

    public OrganizationEntity Organization { get; set; } = null!;
}

ShippingAddress is a jsonb column in PostgreSQL, with the following properties inside :

public class OrderAddress
{
   public string? FirstName { get; set; }
   public string? LastName { get; set; }
   public string? Street { get; set; }
   public string? ZipCode { get; set; }
   public string? City { get; set; }

   public OrderAddressCountry? Country { get; set; }
   public OrderAddressCompany? Company { get; set; }
}

Configuration of ShippingAddress is made using FluentAPI in my dbContext file :

public class OrderManagementDbContext(DbContextOptions<OrderManagementDbContext> options) : DbContext(options)
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<orderEntity>().OwnsOne(
            order => order.ShippingAddress, ownedNavigationBuilder =>
            {
                ownedNavigationBuilder.ToJson();
                ownedNavigationBuilder.OwnsOne(shippingAddress => shippingAddress.Country);
                ownedNavigationBuilder.OwnsOne(shippingAddress => shippingAddress.Company);
            });

        modelBuilder.ConfigureCamelCase();
    }

    //Tables
    public virtual DbSet<OrderEntity> Orders { get; set; }
}

Result

When executing requests against orderEntity, I get the following error :

Npgsql.PostgresException (0x80004005): 42703: column p.ShippingAddress does not exist

When I create a migration, my table reflects this naming convention issue :

migrationBuilder.CreateTable(
    name: "order",
    columns: table => new
    {
        id = table.Column<Guid>(type: "uuid", nullable: false),
        reference = table.Column<string>(type: "text", nullable: false),
        organizationId = table.Column<Guid>(type: "uuid", nullable: false),
        ShippingAddress = table.Column<string>(type: "jsonb", nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("order_pkey", x => x.id);
        table.ForeignKey(
            name: "order_organization_organizationId_fkey",
            column: x => x.organizationId,
            principalTable: "organization",
            principalColumn: "id",
            onDelete: ReferentialAction.Cascade);
    });

I already tried these methods without success:

  1. Globally update JsonSerializerOptions.PropertyNamingPolicy to JsonNamingPolicy.CamelCase

  2. Globally set JsonSerializerOptions.PropertyNameCaseInsensitive to false

  3. Set ShippingAddress name with column data annotation

  4. Set ShippingAddress name with Fluent API HasColumnName function

  5. Set ShippingAddress name in OwnsOne function with ToTable and ExcludeFromMigrations functions

  6. Manually set navigation name in camel case using modelBuilder.Model.GetEntityTypes().GetDeclaredNavigations() and then check for IsOwned() targetEntityType and then use SetField function :

    foreach (var navigationProperty in tableConfiguration.GetDeclaredNavigations())
    {
        if (navigationProperty.TargetEntityType.IsOwned())
        {
            navigationProperty.SetField(navigationProperty.Name.ToCamelCase());
        }
    }
    

    But I get another error while running the mutation:

    Unable to create a 'DbContext' of type ''. The exception 'The specified field 'shippingAddress' could not be found for property 'OrderEntity.ShippingAddress'.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

  7. Same tests than 6. with "GetNavigations()" instead of "GetDeclaredNavigations"

Thanks for your help !

1

There are 1 best solutions below

0
Rémi Lamotte On

Solved with the following choices :

  1. Use EFCore.NamingConventions nuget instead of custom code to have camelCase naming convention applied :
builder.Services.AddDbContext<OrderManagementDbContext>(options =>
         options.UseNpgsql(builder.Configuration.GetConnectionString("LocalConnectionString"))
                .UseCamelCaseNamingConvention());
  1. Make a workaround to fix json owned type properties in camelCase : you can follow my answer on Support Json nested name convertions.