Entity Framework Core Models

This document details how to define and work with models in Entity Framework Core (EF Core). Models represent the shape of your data, typically as C# classes, and how they map to database tables.

Defining Models

EF Core uses Plain Old CLR Objects (POCOs) to represent your data. These are standard C# classes with properties that map to columns in your database tables. EF Core can discover these models in a few ways:

1. Convention-Based Modeling

EF Core uses a set of conventions to infer the database schema from your model classes. If your classes follow these conventions, you often don't need explicit configuration.

public class Product
{
    public int ProductId { get; set; } // Convention-based primary key
    public string Name { get; set; }
    public decimal Price { get; set; }
}

2. Data Annotations

You can use attributes from the System.ComponentModel.DataAnnotations namespace to configure your model more explicitly. This is useful for simple configurations or when you can't modify the model classes (e.g., they are generated).

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

public class Category
{
    [Key] // Explicitly marks this as the primary key
    public int CategoryId { get; set; }

    [Required] // Makes the Name column non-nullable
    [StringLength(100)] // Sets the maximum length of the string column
    public string Name { get; set; }

    // A collection of related products
    public ICollection<Product> Products { get; set; }
}

3. Fluent API

The Fluent API provides the most powerful and flexible way to configure your model. You configure it in the OnModelCreating method of your DbContext. This is the preferred method for complex configurations or when you need fine-grained control.

using Microsoft.EntityFrameworkCore;

public class ApplicationDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>()
            .HasKey(p => p.ProductId); // Explicitly define primary key

        modelBuilder.Entity<Product>()
            .Property(p => p.Name)
            .IsRequired()
            .HasMaxLength(200); // Configure name property

        modelBuilder.Entity<Category>()
            .ToTable("ProductCategories"); // Map to a different table name

        modelBuilder.Entity<Category>()
            .HasMany(c => c.Products) // Define one-to-many relationship
            .WithOne(p => p.Category)
            .HasForeignKey(p => p.CategoryId); // Define foreign key
    }
}

Relationships

EF Core supports mapping common relational database relationships:

Navigation Properties

Navigation properties are used to represent relationships between entities. They can be a single instance of another entity or a collection of another entity.

public class Order
{
    public int OrderId { get; set; }
    public DateTime OrderDate { get; set; }

    // Foreign key property
    public int CustomerId { get; set; }
    // Navigation property for the one-to-many relationship with Customer
    public Customer Customer { get; set; }

    // Navigation property for the one-to-many relationship with OrderItems
    public ICollection<OrderItem> OrderItems { get; set; }
}

public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }

    // Navigation property for the one-to-many relationship with Orders
    public ICollection<Order> Orders { get; set; }
}

public class OrderItem
{
    public int OrderItemId { get; set; }
    public int Quantity { get; set; }

    // Foreign key property
    public int OrderId { get; set; }
    // Navigation property for the many-to-one relationship with Order
    public Order Order { get; set; }

    // Foreign key property
    public int ProductId { get; set; }
    // Navigation property for the many-to-one relationship with Product
    public Product Product { get; set; }
}

Note

When configuring relationships using the Fluent API, ensure you correctly specify the navigation properties and foreign key properties to establish the connections between entities.

Entity States

EF Core tracks the state of entities. When you query an entity, it starts as Unchanged. If you modify it, its state becomes Modified. New entities are Added, and entities marked for deletion are Deleted. The DbContext manages these state transitions.

Value Objects

Value objects are objects that are defined by their attributes rather than an identity. They are typically immutable and are often owned by a single entity. EF Core supports mapping value objects, often by having them owned by an entity. This can be configured using the OwnsOne or OwnsMany methods in the Fluent API.

Tip

Leveraging value objects can lead to more robust and maintainable code by encapsulating related data and behavior.

Further Reading