Home > OS >  EF Core 7 json columns , properties are not serialized
EF Core 7 json columns , properties are not serialized

Time:01-26

I am working on a project using .Net 7 and EF Core 7.0.2. I would like to use the new feature json columns but in the database is persisted wrong.

Here is my model:

[Serializable]
    public class Customer
    {
        public long CustomerId { get; }
        public string CustomerName { get; }
        public CustomerDetail CustomerDetails { get; }
        public DateTime CreatedAt { get; }

        private Customer()
        {
            CustomerDetails = CustomerDetail.Empty;
            CustomerName = string.Empty;
        }

        public Customer(long customerId, string customerName, CustomerDetail customerDetails, DateTime createdAt)
        {
            CustomerId = customerId;
            CustomerName = customerName;
            CustomerDetails = customerDetails;
            CreatedAt = createdAt;
        }
    }

    [Serializable]
    public class CustomerDetail
    {
        private readonly List<Order> _orders;
        public int LanguageId { get; }
        public string CurrencySysname { get; }
        public Card? PaymentMethod { get; }
        public Address? Address { get; }
        public IReadOnlyCollection<Order> Orders => _orders.ToArray();

        private CustomerDetail()
        {
            CurrencySysname = string.Empty;
            _orders = new List<Order>();
        }

        public CustomerDetail(IReadOnlyCollection<Order> orders, int languageId, string currencySysname, Card? paymentMethod, Address? address)
        {
            _orders = orders.ToList();
            LanguageId = languageId;
            CurrencySysname = currencySysname;
            PaymentMethod = paymentMethod;
            Address = address;
        }

        public static readonly CustomerDetail Empty = new CustomerDetail();
    }

    [Serializable]
    public class Address
    {
        public int CityId { get; }
        public int PostalCode { get; }

        private Address()
        {

        }

        public Address(int cityId, int postalCode)
        {
            CityId = cityId;
            PostalCode = postalCode;
        }
    }

    [Serializable]
    public class Card
    {
        public int CardType { get; }

        private Card()
        {

        }

        public Card(int cardType)
        {
            CardType = cardType;
        }
    }

    [Serializable]
    public class Order
    {
        public int OrderId { get; }
        public int Amount { get; }

        private Order()
        {

        }

        public Order(int orderId, int amount)
        {
            OrderId = orderId;
            Amount = amount;
        }
    }

about the database configuration is :

class CustomerConfiguration : IEntityTypeConfiguration<Customer>
    {
        public const string Table = "Customers";

        public void Configure(EntityTypeBuilder<Customer> builder)
        {
            builder.ToTable(Table);
            builder.HasKey(x => x.CustomerId);

            builder.Property(x => x.CustomerId).ValueGeneratedNever().IsRequired();
            builder.Property(x => x.CustomerName).IsRequired();
            builder.Property(x => x.CreatedAt).IsRequired();

            builder.OwnsOne(x => x.CustomerDetails, details =>
            {
                details.ToJson();
                details.OwnsOne(x => x.Address);
                details.OwnsOne(x => x.PaymentMethod);
                details.OwnsMany(x => x.Orders);
            });
        }
    }

and when I generate the migration the result is that it semms correct :

public partial class Addcustomer : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Customers",
                columns: table => new
                {
                    CustomerId = table.Column<long>(type: "bigint", nullable: false),
                    CustomerName = table.Column<string>(type: "nvarchar(max)", nullable: false),
                    CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
                    CustomerDetails = table.Column<string>(type: "nvarchar(max)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Customers", x => x.CustomerId);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Customers");
        }
    }

Finally when i am going to write in the database

[HttpGet("AddCustomer")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        public async Task<IActionResult> AddCustomerAsync()
        {
            var orders = new List<Order>()
            {
                new Order(orderId: 1, amount: 4),
                new Order(orderId: 2, amount: 5),
            };

            var customer = new Customer(
                customerId: 123,
                customerName: "Jim",
                new CustomerDetail(
                    orders: orders,
                    languageId: 3,
                    currencySysname:"EUR",
                    paymentMethod: new Card(cardType: 1),
                    address: new Address(cityId: 3, postalCode: 123)),
                createdAt: DateTime.Now);

            using var db = new TestDbContext(_options);
            db.Customers.Add(customer);

            await db.SaveChangesAsync();

            return Ok($"Pong - {DateTime.UtcNow:o}");
        }

the result in the database is enter image description here

Edit:

You didn't use set on the property, which means it only has read-only properties, so you can't set its value, so the field you save in database is empty.

You have to keep in mind that a property is just syntactic sugar for a pair of methods. One method (the getter) returns a value of the property type and one method (the setter) accepts a value of the property type.

So, there isn't really any way in general to "set" a property that doesn't have a setter.

Refer to Kyle's answer.

CodePudding user response:

Also open a ticket in ef core repo and microsoft team answers in this question that :

Only read-write properties are mapped by convention. (But see #

4356.) Any property can be mapped by including it explicitly in the entity type mapping. For example:

details.Property(x => x.CurrencySysname);
  • Related