Mapping Value Objects with Entity Framework
Value objects are small objects that represent a simple entity whose equality is based on the values of their properties. In Entity Framework, value objects can be mapped to database tables in order to store their values. This guide describes how to map value objects with Entity Framework, with examples and tips.
Basic Setup
First, you need to define the value object as a class in your domain model. The value object should have properties that define its state. For example:
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
Next, you need to map the value object to a database table. This is done by using the EntityTypeConfiguration
class, which is provided by Entity Framework. For example:
public class AddressMap : EntityTypeConfiguration<Address>
{
public AddressMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Street);
Property(t => t.City);
Property(t => t.State);
Property(t => t.ZipCode);
}
}
Finally, you need to register the mapping in the DbContext
class. This is done by calling the Map
method of the DbModelBuilder
class. For example:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AddressMap());
}
Advanced Mapping Concepts
Entity Framework provides a number of advanced mapping concepts that can be used to further customize the mapping of value objects. These include table splitting, table per type (TPT) inheritance and stored procedures.
Table Splitting
Table splitting is a technique that can be used to separate the properties of a value object into multiple database tables. This allows you to store related data in separate tables, which can improve query performance. For example, if you have a Address
value object that contains a Street
and a City
property, you can split the object into two tables: one for the Street
and one for the City
.
Table splitting is configured by using the Map
method of the EntityTypeConfiguration
class. For example:
public class AddressMap : EntityTypeConfiguration<Address>
{
public AddressMap()
{
// Primary Key
HasKey(t => t.Id);
// Street Table
Map(t => t.ToTable("Address_Street"));
Property(t => t.Street);
// City Table
Map(t => t.ToTable("Address_City"));
Property(t => t.City);
Property(t => t.State);
Property(t => t.ZipCode);
}
}
Table per Type (TPT) Inheritance
Table per type (TPT) inheritance is a technique that can be used to map an object hierarchy to a database. This allows for a single table to contain multiple types of objects. For example, if you have a Person
class that contains both a Customer
and an Employee
subclass, you can map them to a single Person
table.
Table per type inheritance is configured by using the Map
method of the EntityTypeConfiguration
class. For example:
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Name);
// Discriminator
Map(t => t.Requires("PersonType").HasValue("Person"));
}
}
public class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Name);
Property(t => t.Discount);
// Discriminator
Map(t => t.Requires("PersonType").HasValue("Customer"));
}
}
public class EmployeeMap : EntityTypeConfiguration<Employee>
{
public EmployeeMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Name);
Property(t => t.Salary);
// Discriminator
Map(t => t.Requires("PersonType").HasValue("Employee"));
}
}
Stored Procedures
Stored procedures can be used to execute database operations, such as inserting or updating data. Stored procedures can be mapped to value objects by using the MapToStoredProcedures
method of the EntityTypeConfiguration
class. For example:
public class AddressMap : EntityTypeConfiguration<Address>
{
public AddressMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
Property(t => t.Street);
Property(t => t.City);
Property(t => t.State);
Property(t => t.ZipCode);
// Stored Procedure
MapToStoredProcedures(s =>
s.Insert(i => i.HasName("InsertAddress"))
.Update(u => u.HasName("UpdateAddress"))
.Delete(d => d.HasName("DeleteAddress")));
}
}
Tips
- When mapping value objects, use the
EntityTypeConfiguration
class to customize the mapping. - Table splitting can be used to improve query performance by storing related data in separate tables.
- Table per type (TPT) inheritance can be used to map an object hierarchy to a single database table.
- Stored procedures can be used to execute database operations for value objects.