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}");
}
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);