RemyDuijkeren / NodaMoney

NodaMoney provides a library that treats Money as a first class citizen and handles all the ugly bits like currencies and formatting.
Apache License 2.0
211 stars 45 forks source link

Serialize Money with Entity Framework Core 2.0 #52

Open ghost opened 7 years ago

ghost commented 7 years ago

Hi, Since Noda's Money is a struct type, we cannot use it as field type for our entities in our .Net Core 2.0 project, using Entity Framework Core 2.0.

Since EF Core 2.0 now fully supports owned entities, it would be great to be able to do that!

Could you advise us a way to do that? Thanks

bugproof commented 6 years ago

Yes, I'm just thinking as well how to integrate it with EF Core, I will somehow need to persist all the currencies in the database to refer to them in the other entities. I started to create all those money entities myself but I don't think it's reasonable when a great library like NodaTime exists.

andriysavin commented 4 years ago

Owned entities have mostly the same traits as regular entities, so they are required to be reference types (so the change tracker can track them). A value convertor is not an option as well, because it can't convert an object to multiple objects (e.g. amount and currency) so they could be mapped to multiple DB columns. I had to wrap Money struct into my own reference type (configured as owned entity) to work around this.

chrisbbe commented 3 years ago

@andriysavin Interesting approach, it seems like the EF team will provide support for value converters to map to multiple DB columns in the release of EF Core 6.

Meanwhile, are you interested in sharing your Money wrap workaround code?

andriysavin commented 3 years ago

@chrisbbe It's as simple as this:

public sealed class Price
        : IComparable<Price?>, IEquatable<Price?>
{
        private readonly Money price;

        public decimal Amount => price.Amount;
        public string Currency => price.Currency.Code;

        public Price(Money price)
        {
            this.price = price;
        }

        public Price(decimal amount, string currency)
        {
            price = new Money(amount, currency);
        }
...........
...........
}

..........
// In DbContext configuration  for enclosing entity (Product)

b.OwnsOne(prod => prod.Price, o =>
{
 // Read-only properties are not mapped by convention.
 // For decimal in SqlServer you should explicitly specify
 // the column type to avoid warning.
 o.Property(price => price!.Amount).HasColumnType("decimal(18,2)");
 o.Property(price => price!.Currency);
})