MSDN Documentation

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:

Note: The exact database type and its precision/scale can vary depending on the database provider being used (e.g., SQL Server, PostgreSQL, MySQL, SQLite).

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.

Tip: Using owned entities for value objects promotes better code organization and encapsulation, as these objects are conceptually part of the parent entity.

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

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.