MSDN Documentation

Microsoft Developer Network

Modeling Data with Entity Framework Core

This section guides you through the process of defining your domain models and configuring how Entity Framework Core (EF Core) maps them to your database.

Core Concepts

EF Core uses conventions and explicit configuration to understand your domain classes and translate them into database schema. The primary building blocks are:

  • Entities: Plain Old CLR Objects (POCOs) that represent data in your application.
  • DbSets: Collections of entities available through your DbContext.
  • DbContext: The main class for interacting with the database. It represents a session with the database and allows you to query and save data.

Conventions

EF Core applies a set of default conventions to infer the database schema from your model. Some common conventions include:

  • Public properties with a getter and setter of a specific type are considered properties of the entity.
  • Properties named Id or <ClassName>Id are treated as the primary key.
  • Navigational properties to other entities are treated as foreign key relationships.

Configuring the Model

While conventions are useful, you'll often need to provide explicit configuration to customize your model mapping. This can be done in two main ways:

1. Data Annotations

You can use attributes from the System.ComponentModel.DataAnnotations namespace to configure your model directly on your entity classes.


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

public class Product
{
    [Key] // Marks this property as the primary key
    public int ProductId { get; set; }

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

    [Column(TypeName = "decimal(18, 2)")] // Specifies the database column type
    public decimal Price { get; set; }

    public int CategoryId { get; set; }
    [ForeignKey("CategoryId")] // Configures the foreign key relationship
    public Category Category { get; set; }
}

public class Category
{
    public int CategoryId { get; set; }
    public string Name { get; set; }
    public ICollection<Product> Products { get; set; }
}
                

2. Fluent API

The Fluent API provides a more powerful and centralized way to configure your model within the OnModelCreating method of your DbContext.


using Microsoft.EntityFrameworkCore;

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

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>(entity =>
        {
            entity.HasKey(e => e.ProductId); // Primary key configuration

            entity.Property(e => e.Name)
                .IsRequired() // Non-nullable
                .HasMaxLength(255); // Max length

            entity.Property(e => e.Price)
                .HasColumnType("decimal(18, 2)"); // Column type

            entity.HasOne(p => p.Category) // One-to-many relationship
                  .WithMany(c => c.Products)
                  .HasForeignKey(p => p.CategoryId); // Foreign key property
        });

        modelBuilder.Entity<Category>(entity =>
        {
            entity.HasKey(e => e.CategoryId);
            entity.Property(e => e.Name).IsRequired();
        });
    }
}
                
Note: It's generally recommended to use the Fluent API for most configurations, especially for complex models, as it keeps your entity classes cleaner. Data Annotations are useful for simple, common configurations.

Relationships

EF Core supports mapping common relational database concepts:

  • One-to-One: A single instance of entity A is related to a single instance of entity B.
  • One-to-Many: A single instance of entity A can be related to multiple instances of entity B.
  • Many-to-Many: Multiple instances of entity A can be related to multiple instances of entity B. EF Core automatically creates a linking table for this relationship.

Relationships are typically defined using navigational properties and foreign key properties.

Complex Types and Value Objects

EF Core allows you to model complex data structures that don't have their own primary key by treating them as owned types or value objects. This can help in organizing your domain model without creating separate tables for every component.

Tip: Use owned types to model data that is logically part of another entity and cannot exist independently.

Table Splitting

If you have a single entity that logically spans multiple database tables (e.g., for performance or organization), EF Core supports table splitting. This maps multiple tables to a single entity.

Owned Types

Owned types allow you to model complex properties that do not have their own identity. They are owned by another entity and share the same primary key. This is useful for representing address, contact information, etc.


public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public Address ShippingAddress { get; set; } // Owned type
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string PostalCode { get; set; }
}
                

In the DbContext:


protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Customer>().OwnsOne(c => c.ShippingAddress);
}
                

Database Generation

Once your model is defined, EF Core can generate the database schema for you using Migrations. This process involves creating migration files that represent changes to your database schema over time.

Important: Always review the generated SQL for migrations before applying them to a production database.

Next Steps

Continue by exploring how to query data efficiently using EF Core and manage data changes through migrations.