Entity Framework custom type as property
I have a custom type like:
public class Date { public int Year {get; } public int Month {get; } public int Day {get; } private Date(int year, int month, int day) { // ... Year = year, Month = month, Day = day } public static Date Of(string pattern) { // ... to parse from pattern // return new Date(year, month, date); } public override string ToString(){ // ... string like "2020-02-02" } } }
I want to use it in a Entity to store it in database as string:
public class Student{ public string Name{get; set; } public Date CreateDate{get; set; } public DateTime CreateDate2{get; set; } }
I have a DateConverter
to covert the Date
object to string:
class DateConverter : ValueConverter<Date, string> { public DateConvertor(ConverterMappingHints? mappingHints = null) : base(date => date.ToString(), s => Date.Of(s), mappingHints) { } }
But I could not apply the DateConvertor.
EF Core treat Date as an Entity Type, instead of a property of an Entity.
That means:
protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) { // "Date" appears here as an Entity _logger.Info("Model Entity: {0}", entityType); foreach (IMutableProperty property in entityType.GetProperties()) { // I want "Date" appears here as a property. // Actually "DateTime CreateDate2" appears here. // I want my "Date" to act like "DateTime"
How to make custom type Date
act like a DateTime
as a property, instead of a entity type?
Or
How to apply DateConverter
to my Date
?
Update: This is one of the examples.
Sorry, I could not share my real code because they belong to my company.
But the problem is the same.
I want to know how to handle all this kind of problems.
What if I have a Metadata, to store it as string (or number) in database?
public class Metadata { public int Field1 {get; } public string Filed2 {get; } public double Field3 {get; } private Metadata(int xxx /* other params */ ) { // to assign all } public static Metadata Parse (string pattern) { // ... to parse from pattern // return new Metadata(xxx); } public override string ToString(){ // ... string like "abc.id]afe[dfe]fefa" // using a special encoding } } class MetadataConverter : ValueConverter<Metadata, string> { public MetadataConverter(ConverterMappingHints? hints = null) : base(data => data.ToString(), data => Metadata.Parse(data), mappingHints) { } }
EF Core treat Date as an Entity Type, instead of a property of an Entity.
Only in this situation could I use DateTime instead, no problem. But could I find out an alternate solution every time?
Rather than convert it to a DateTime, which may work in this case but not all cases, you can register your value object as a complex type
This means that EF will interpret your Date
object as a "bag of properties" of the Student
object that owns it, rather than as a separate entity of its own. In short, it will create a table column (in the parent entity table, e.g. [Students]
) for every public property (day, month, year).
However, that does come with the requirement of needing to have setters on your properties and a parameterless constructor, otherwise EF cannot render your value object from the database.
In cases where there is no equivalent data type that EF can handle (such as DateTime
for Date
), this is the better option.
Just configure the ValueConverter in OnModelCreating, like this:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); var dateConverter = new ValueConverter<Date, string>( v => v.ToString(), s => Date.Parse(s) ); foreach (var et in modelBuilder.Model.GetEntityTypes()) { foreach (var prop in et.GetProperties().Where(prop => prop.ClrType == typeof(Date))) { prop.SetValueConverter(dateConverter); //modelBuilder.Entity(et.Name).Property(prop.Name).HasConversion(dateConverter); } } }