.NET Entity Framework Core

Mastering Data Modeling

Entity Framework Core: Data Modeling

Entity Framework Core (EF Core) provides a powerful and flexible way to model your application's data using Plain Old CLR Objects (POCOs). This section delves into the core concepts and techniques for defining your entities, relationships, and database schema.

Core Concepts

  • Entities: Represent tables in your database. Each property of an entity typically maps to a column.
  • DbContext: The primary class used to interact with the database. It represents a session with the database and can be used to query and save data.
  • DbSet<TEntity>: Represents a table in the database. You'll typically have a `DbSet<TEntity>` for each entity in your context.

Defining Your Entities

You define your entities as C# classes. EF Core uses conventions and, optionally, data annotations or the Fluent API to map these classes to database structures.

Simple Entity Example


public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}
                    

Configuring Your Model

EF Core uses a convention-over-configuration approach. However, you often need to customize this mapping.

1. Data Annotations

Attributes applied directly to your entity classes.

Data Annotations Example


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

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    [Required]
    [MaxLength(100)]
    public string Name { get; set; }

    [Column("Description", TypeName = "text")]
    public string Details { get; set; }
}
                    

Key annotations include:

  • [Key]: Designates the primary key property.
  • [Required]: Marks a property as non-nullable in the database.
  • [MaxLength(n)]: Sets the maximum length for string properties.
  • [Column(...)]: Maps a property to a specific column name, type, or order.

2. Fluent API

Configured within the OnModelCreating method of your DbContext. This is the most powerful and recommended way for complex configurations.

Fluent API Example


using Microsoft.EntityFrameworkCore;

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .HasKey(b => b.BlogId); // Primary Key

        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired()
            .HasMaxLength(200);

        modelBuilder.Entity<Post>()
            .ToTable("Posts", t => t.HasComment("Stores blog posts")); // Table name and comment

        modelBuilder.Entity<Blog>()
            .HasMany(b => b.Posts) // One-to-Many relationship
            .WithOne(p => p.Blog)
            .HasForeignKey(p => p.BlogId);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; } // Foreign Key
    public Blog Blog { get; set; }
}
                    

Common Fluent API configurations include:

  • .ToTable("TableName"): Specifies the table name.
  • .HasKey(p => p.PropertyName): Configures a composite or simple primary key.
  • .Property(p => p.PropertyName).IsRequired(): Sets a property as required.
  • .Property(p => p.PropertyName).HasMaxLength(n): Sets the maximum length.
  • .HasColumnName("ColumnName"): Sets the column name.
  • .HasColumnType("TypeName"): Sets the database column type.
  • .HasRelationship(...): Configures relationships between entities.

Relationships

EF Core supports modeling one-to-one, one-to-many, and many-to-many relationships. These are typically defined using navigation properties and configured via the Fluent API or data annotations.

  • One-to-Many: A blog can have many posts, but a post belongs to only one blog.
  • Many-to-Many: A post can have many tags, and a tag can be associated with many posts. This requires a "shadow" or "join" table.
  • One-to-One: A user might have one profile.

Advanced Modeling Features

  • Owned Types: Model types that don't have their own table but are owned by another entity.
  • TPH (Table-Per-Hierarchy): Stores all types in an inheritance hierarchy in a single table.
  • TPC (Table-Per-Concrete-Type): Stores each concrete type in its own table.
  • TPTS (Table-Per-Type-Sharing): Stores each entity type in its own table, but shares common columns between related types.
  • Value Objects: Use owned types to model immutable value objects.
  • Shadow Properties: Properties that are not defined on your entity classes but are defined in the EF Core model.
  • Value Converters: Customize how properties are converted to and from database types.

Understanding how to effectively model your data is crucial for building robust and efficient applications with Entity Framework Core. Explore the official Microsoft documentation for in-depth details on each of these features.