Entity Framework Core: Core Features

This document explores the fundamental features that make Entity Framework Core (EF Core) a powerful and versatile Object-Relational Mapper (ORM) for .NET developers.

1. LINQ to Entities

LINQ to Entities allows you to write queries against your data using Language Integrated Query (LINQ). EF Core translates these LINQ queries into SQL that is executed against the database.


using (var context = new MyDbContext())
{
    var customers = from c in context.Customers
                    where c.City == "London"
                    select c;

    foreach (var customer in customers)
    {
        Console.WriteLine(customer.CompanyName);
    }
}
            

This feature significantly enhances developer productivity by allowing them to work with data using familiar C# or VB.NET syntax, rather than writing raw SQL strings.

2. Change Tracking

EF Core automatically tracks changes made to entities that are loaded into the context. When SaveChanges() is called, EF Core determines what SQL statements need to be executed to persist these changes to the database.


using (var context = new MyDbContext())
{
    var product = context.Products.Find(1);
    if (product != null)
    {
        product.Price = 19.99M; // EF Core will detect this change
        context.SaveChanges(); // Generates an UPDATE statement
    }
}
            

EF Core supports tracking changes for entities in three states: Unchanged, Added, Modified, and Deleted.

3. Relationships and Navigation Properties

EF Core makes it easy to model and query relationships between entities, such as one-to-one, one-to-many, and many-to-many. Navigation properties simplify accessing related entities.


// Assuming Customer has a collection of Orders (one-to-many)
var firstCustomer = context.Customers.Include(c => c.Orders).FirstOrDefault();

if (firstCustomer != null)
{
    foreach (var order in firstCustomer.Orders)
    {
        Console.WriteLine($"Order ID: {order.OrderId}, Date: {order.OrderDate}");
    }
}
            

EF Core can automatically configure these relationships based on conventions or explicit configuration using the Fluent API or Data Annotations.

4. Database Provider Model

EF Core is designed with a pluggable database provider model. This means EF Core can interact with various database systems, including SQL Server, PostgreSQL, MySQL, SQLite, and more, without changing your application code.

You typically configure the database provider in your DbContext's OnConfiguring method or by using dependency injection.


protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer("YourConnectionString");
    // or optionsBuilder.UseNpgsql("YourConnectionString");
    // or optionsBuilder.UseSqlite("Data Source=my-database.db");
}
            
Tip: Choose the provider that best suits your project's needs and deployment environment.

5. Migrations

EF Core Migrations provide a way to incrementally update your database schema as your application's data model evolves. They allow you to manage database changes in a version-controlled manner.

Common commands include:


// Example of a generated migration file (simplified)
public override void Up()
{
    CreateTable(
        name: "Products",
        columns: table => new
        {
            ProductId = table.Column<int>(nullable: false)
                .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
            Name = table.Column<string>(nullable: true),
            Price = table.Column<decimal>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Products", x => x.ProductId);
        });
}
            
Note: Migrations are essential for production environments to ensure database schema consistency.

6. Entity States and DbContext Lifetime

Understanding the lifetime of a DbContext and the states of entities is crucial for efficient data management. A DbContext instance is typically designed to be short-lived and used within a single unit of work.

Entity States:

Important: Avoid long-lived DbContext instances, as they can lead to performance issues and stale data. Use dependency injection to manage their lifetime effectively.