I've got a DTO which should be stored in a database and also have the ability to be serialized to JSON.
I've developed a method allowing me to serialize and deserialize multiple different inheritors of an abstract class at once, which required me to add a discriminator to the base class (which just returns GetType().FullName).
It'd be great to be able to use this as a discriminator for the Database as well, rather than having two different discriminators in the same class.
Here's an example of what I've got:
[JsonConverter(typeof(BaseClassConverter))]
public abstract class MyBaseClass
{
...
public string FullName => GetType().FullName;
...
}
// (FullName property returns My.Namespace.Class1)
public class Class1 : MyBaseClass
{
...
}
// (FullName property returns My.Namespace.Class2)
public class Class2 : MyBaseClass
{
...
}
I've tried configuring this as a discriminator, but EF wants to set the discriminator value itself (No backing field could be found for property 'FullName' of entity type 'DataPoint' and the property does not have a setter.). Is it possible to tell EF to use this property as a discriminator without setting it?
@EoinAviation +1 - Try a protected setter.
By the way, as a polite code review, do not use class names as your discriminator. If someone wants to later rename your classes, they can't. It's better to create unique keys for each entity.
@EoinAviation You can use a property with a no-op private setter (does not need to be protected), but you should make sure that the values it returns correspond to the discriminator values configured in the EF model. In other words, calling the setter should really be a no-op because the value of the property already contains the value that would have been set.
@ajcvickers Could the CoreStrings.resx file's resource namd NoFieldOrSetter be suffixed to say "Use a property with a no-op private setter or protected setter."?
P.S. Amused by downvoting my comment. But even if the developers remember to write a migration to update the cell data in the table column in conjunction with a rename refactoring, for a VLDB that could be a VERY expensive operation (the transaction log space alone to convert several hundred bytes of text data into say a 4 byte C# int worries me). It's best to solve such problems by avoiding them, in my experience :)
@jzabroski I'm not sure why we would update the message in that way. For this specific case, that can work with care, but it certainly isn't a general solution for that exception.
I downvoted your comment because it my opinion that's over-engineering. You may have specific reasons for being careful about this, but for most people it falls into YAGNI.
I don't intend for it to be a general solution - just a hint. I submitted a PR #15538 which has slightly different verbiage - I wrote as a hint that if the property is a readonly property, to consider the advice we've both suggested here.
I have not done much EFCore since November, but now that I am back at it, I am finding some of the errors hard to understand and my only saving grace is looking at the test coverage and what behaviors the framework is enforcing. I've spent all day writing my own tests to contribute back, and to be honest, I keep double and triple checking them because the error messages are so bad that I have to assume the problem is me missing something obvious, not EntityFrameworkCore. (To be fair, I think some error messages make sense IF you worked on EF6 and understand the most common model mapping issues developers had where there were two classes with the same ShortName in the same AppDomain, as was frequently common in projects where you had co-existing Linq2Sql-legacy and EF6 data models. The new EFCore Fluent API prevents that, but creates a new set of issues where if you call something the wrong way, you get weird messages.)
Either way, do with you wish with my feedback.
@ajcvickers Related to our discussion in #15538 (now closed by me after your feedback), I have found that this is actually documented, just in a different topic: https://docs.microsoft.com/en-us/ef/core/modeling/constructors#read-only-properties
To save you time, here is verbatim quote from that section:
Once properties are being set via the constructor it can make sense to make some of them read-only. EF Core supports this, but there are some things to look out for:
- Properties without setters are not mapped by convention. (Doing so tends to map properties that should not be mapped, such as computed properties.)
- Using automatically generated key values requires a key property that is read-write, since the key value needs to be set by the key generator when inserting new entities.
An easy way to avoid these things is to use private setters.
@AndriySvyryd I like @MatthewMWR observation of documenting all scenarios of read-only properties:
Note that there are several issues open related to read-only properties (list below). I don't mean to duplicate those, but rather ask what the intended/suggested pattern is here and if this isn't supported today to consider it for the future.
1527
1311
1526
2318
As I mention in my previous comment, read-only properties are in fact documented - the issue is lack of linkback from other parts of EF documentation enabling this documentation to be more discoverable / findable from Google (and Bing :))