When using Value Objects with Entity Framework Core, you often want to store them as simple types (like string or decimal) in the database while using the rich VO type in your domain model.
EF Core ValueConverter classes allow you to define how a VO is converted to and from its database representation.
For any VO that wraps a single value (like Email or UserId), you should implement a ValueConverter.
public class EmailValueConverter : ValueConverter<Email, string>
{
public EmailValueConverter()
: base(vo => vo.Value, value => new Email(value))
{
}
}
You can register these converters in the OnModelCreating method:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(builder =>
{
builder.Property(u => u.Email)
.HasConversion<EmailValueConverter>()
.HasMaxLength(256);
});
}
For VOs with multiple properties (like LatLng or Payment), use EF Core Owned Entities.
modelBuilder.Entity<Job>(builder =>
{
builder.OwnsOne(j => j.Location, cb =>
{
cb.Property(l => l.Latitude).HasColumnName("Latitude");
cb.Property(l => l.Longitude).HasColumnName("Longitude");
});
});
In large projects, you can use reflection to automatically apply converters to all properties that match a specific VO type.
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder
.Properties<Email>()
.HaveConversion<EmailValueConverter>();
configurationBuilder
.Properties<UserId>()
.HaveConversion<UserIdValueConverter>();
}
This ensures that every Email property in every entity is automatically handled without manual configuration for each one.