Issue #10703 covers materialization of entities with constructors, which allows immutable entities to be used with EF Core. However, since these entities are immutable it means that they are read-only from an EF perspective--that is, the database row will never get updated. This issue is about enabling patterns in EF that are commonly used with immutable objects, such as creating new instances to represent changes. See also discussion in #3129.
Some notes from a recent discussion:
One way EF Core could work with immutable objects is that immutability also extends to the database, so you can insert, and maybe delete, but never ever update any row.
Another approach that seems more pragmatic would be to provide a way to explicitly replace one tracked immutable object with another one with the same key (or if the key is in shadow state, just replace it assuming it has the same key).
It is possible that this coudl be implemented by extending other other functionality that already exists in EF Core in some form or at least are in our roadmap. Here are some ideas/questions:
What if replace is handled as just a delete plus an insert?
Our ability to make sense of an object deleted and another inserted with the same key in the same unit if work has been evolving recently. We keep finding cases in which we need to improve our topological sort, for example because things like owned entities already can be replaced.
What if we could extend this from owned to “root” objects?
Then what if EF Core’s update pipeline had the ability to fuse a delete and an insert on the same key into a database update?
What happens to the graphs of objects connected to the object replaced and the replacement? Maybe aggregates help rationalize things?
Should Attach stop throwing that there is a tracker object with the same key and the object is immutable? Or should we require that something more explicit, like a new Replace method, is called? Right now I am leaning towards the latter, I think currently most times Attach throws not because you want Replace but because you are doing it wrong.
Is there any update on plans for supporting immutable entities?
@richardpawson This issue is in the Backlog milestone. This means that it is not going to happen for the 3.1 release. We will re-assess the backlog following the 3.1 release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.
With C# 9 records, this becomes a bit more relevant.
Apart from allowing the user to replace a tracked instance with another (Replace or non-throwing Attach), we could imagine a proxy-based system that would leverage that. For example, the With methods on record types could be wrapped so that it calls Replace on the DbContext with the new instance.
@ajcvickers Please please please if and when you look at this, spend just a tiny bit of time getting this working with F# records too. Under the hood, F# records are just C# classes with a single non-default constructor that has parameters for every field on the record, and getter-only properties for each field.
This is probably one of the main issues stopping people using EF on F# - there's just too much friction at the moment.
@isaacabraham we unfortunately probably won't have time to look anytime soon, but it would be useful to know more about how mutation works with F# records. The new C# records have the with pattern, does something similar exist in F#?
Actually, C# copied records from F#
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/copy-and-update-record-expressions
OK, thanks. It's safe to say that if/when we do get around to supporting updates for immutable types, that would include C# records, and therefore probably also support F# records.
@roji great. Just to clarify, by "probably", does you mean that either:
Thanks!
@isaacabraham it's too early to say exactly how we'll approach this and what scope we'll have, as we don't plan to work on this right away. However, assuming there isn't going to be a major, fundamental difference between F# and C# record support, I indeed expect us to specifically target F# support as well.
Most helpful comment
With C# 9 records, this becomes a bit more relevant.
Apart from allowing the user to replace a tracked instance with another (Replace or non-throwing Attach), we could imagine a proxy-based system that would leverage that. For example, the With methods on record types could be wrapped so that it calls Replace on the DbContext with the new instance.