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.
- Entity Types: Any public class that isn't abstract and has a public constructor is considered an entity type.
- Primary Keys: Properties named
Id
or<ClassName>Id
(e.g.,OrderId
for anOrder
class) are convention-based primary keys. - Property Mapping: Public properties with primitive types (like
int
,string
,DateTime
) or complex types that EF Core can map to database types are considered properties.
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:
- One-to-One: A single instance of entity A relates to a single instance of entity B.
- One-to-Many: A single instance of entity A relates to multiple instances of entity B.
- Many-to-Many: Multiple instances of entity A relate to multiple instances of entity B. This typically requires a joining table.
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.