Modeling Data Types and Value Objects
Entity Framework Core (EF Core) provides a rich set of tools and conventions for mapping your .NET entity types to a relational database schema. This section focuses on how EF Core handles data types and the concept of value objects.
Understanding Data Type Mapping
EF Core automatically maps .NET data types to their corresponding database column types. This mapping is based on conventions but can be customized using the Fluent API or data annotations.
Default Mappings
Here are some common default mappings:
int
maps toINT
(or equivalent integer type in your database).string
maps toVARCHAR
orNVARCHAR
(often with a default length).DateTime
maps toDATETIME
orTIMESTAMP
.bool
maps toBIT
.Guid
maps toUNIQUEIDENTIFIER
.decimal
maps toDECIMAL
orNUMERIC
.
Configuring Data Types
Using the Fluent API
The Fluent API offers fine-grained control over property configurations. You can specify the exact database type and its properties.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Price)
.HasColumnType("decimal(18, 2)"); // Specify precision and scale
modelBuilder.Entity<User>()
.Property(u => u.Username)
.HasColumnType("varchar(50)"); // Specify max length
}
Using Data Annotations
Data annotations can be applied directly to your entity classes for simpler configurations.
using System.ComponentModel.DataAnnotations.Schema;
public class Order
{
public int Id { get; set; }
[Column(TypeName = "decimal(10, 2)")]
public decimal OrderTotal { get; set; }
[Column(TypeName = "varchar(255)")]
public string Description { get; set; }
}
Value Objects
Value objects are objects that are defined by their attributes rather than an identity. They are immutable and should not be tracked for changes independently. EF Core supports modeling value objects as owned entities.
Owned Entities
Owned entities are always owned by another entity and do not have their own primary key. They are typically mapped to the same table as their owner or to a separate table if configured.
Defining an Owned Entity
You can define a value object as a separate class and then configure it as an owned type in your DbContext
.
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Configuring Owned Entities (Fluent API)
Use the OwnsOne
or OwnsMany
methods to configure owned types.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().OwnsOne(c => c.ShippingAddress);
modelBuilder.Entity<Customer>().OwnsOne(c => c.BillingAddress);
}
By default, owned entities are configured to share the table with their owner. The properties of the owned entity become columns in the owner's table, often prefixed with the owned entity's property name.
Complex Types as Owned Entities
EF Core also allows you to model complex types (like Address) as owned entities, even if they don't have their own constructors or explicit identity. This is the recommended approach for modeling value objects in EF Core.
Key Concepts
- Immutability: Value objects should ideally be immutable.
- No Identity: Value objects do not have a unique identifier.
- Equality: Equality is based on the values of their properties.
By effectively modeling data types and embracing the concept of value objects, you can create more robust and maintainable data access layers with EF Core.