vkhorikov / CSharpFunctionalExtensions

Functional extensions for C#
MIT License
2.4k stars 301 forks source link

Serializable Maybe? #115

Open space-alien opened 5 years ago

space-alien commented 5 years ago

I have a Maybe property on an entity that I want to persist to DB. I'm using EF Core, and struggling with some of its peculiarities. I experimented with Owned Types, but EF wouldn't play nice with an owned Maybe. (Perhaps the fact that Maybe is a struct is part of it, but I think there are wider issues there, as my experience with Owned Types has been disappointing to say the least).

Value Conversions seemed more promising, but EF won't let you convert nulls (!), so a direct converter for someMaybe.Value is out of the question too.

The best solution I've found so far is to serialize the Maybe to a string, still using a Value Conversion, thus circumventing the null limitation at the expense of a bit of extra cruft in the DB column.

Does a serializable Maybe sound like a useful addition?

Or maybe it sounds like I'm on the wrong track?! (slaps thigh)

PS: In this case, my Maybe is just some ValueObject, but I wonder, how do you tend to encapsulate an optional relationship property on a domain model, when a clumsy ORM like EF is involved? I can't imagine having much luck getting Maybe<T> to work when T is a related entity...

vkhorikov commented 5 years ago

I didn't realize you can't use Value Conversions to convert value objects to nulls, that looks like a pretty basic necessity.

Serialization may help but only if you serialize Maybe.None into null, Maybe.Some into Some and vice versa. I.e. Maybe<string> m = "123" should become "123", and Maybe<string> m = null - null. Default JSON serialization (something like {Value: "123"}) won't play nice with a relational database.

You could also try to do a separate private/internal property of type Entity and use Maybe<Entity> as a wrapper on top of it. Not sure it would work, though. In NHibernate, I normally do a backing field with this mapping:

Map(x => x.RelatedEntity).CustomType<EntityType>().Access.CamelCaseField(Prefix.Underscore);

space-alien commented 5 years ago

Serialization may help but only if you serialize Maybe.None into null, Maybe.Some into Some and vice versa. I.e. Maybe<string> m = "123" should become "123", and Maybe<string> m = null - null. Default JSON serialization (something like {Value: "123"}) won't play nice with a relational database.

It would certainly be the appropriate way to do it for a clean column. Sadly, it will run into the EF Core null Value Conversion limitation.

You could also try to do a separate private/internal property of type Entity and use Maybe<Entity> as a wrapper on top of it. Not sure it would work, though.

I was toying with the same idea just as you replied :) Gave up when I realised that of course EF requires the backing field and the public property to be the same type. To work around that, we're back to Value Conversions.

Sigh. For now I'm going to write my own null-avoiding ValueConverters for value objects, and for optional relationships I guess we can just wait for EF Core 3 and hope it somehow makes good use of nullable reference types in C# 8... 😬

vkhorikov commented 5 years ago

That's quite a bummer. Another possible solution is to switch to NHibernate ;-)

space-alien commented 5 years ago

Next project I start.. definitely checking it out :)