Entity Framework Core Advanced Topics

Advanced Topics

This section covers advanced features and techniques for Entity Framework Core (EF Core), enabling you to optimize performance, handle complex scenarios, and customize EF Core's behavior.

1. Raw SQL Queries

Sometimes, you may need to execute raw SQL queries directly against the database. EF Core provides methods to achieve this, offering maximum flexibility.

var users = context.Users.FromSqlRaw("SELECT * FROM Users WHERE Active = 1");
using (var command = context.Database.GetDbConnection().CreateCommand())
{
    command.CommandText = "SELECT COUNT(*) FROM Orders";
    context.Database.OpenConnection();
    var count = command.ExecuteScalar();
}

2. Database-Generated Values

EF Core supports various database-generated values, including sequences, identity columns, and computed columns. Understanding how to configure these is crucial for data integrity.

  • Identity Columns: Typically used for primary keys.
  • Sequences: A database object that generates unique numbers.
  • Computed Columns: Values computed by the database based on other columns.

Configuration can be done using data annotations or the Fluent API.

modelBuilder.Entity<Product>()
    .Property(p => p.UniqueCode)
    .HasDefaultValueSql("NEWID()");

3. Global Query Filters

Global query filters are applied to all queries for a specific entity type. This is commonly used for implementing soft-delete patterns or tenant isolation.

public class MyDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>().HasQueryFilter(p => !p.IsDeleted);
    }
}

4. Interceptors

Interceptors allow you to intercept and modify EF Core's behavior, such as logging SQL commands, modifying parameters, or handling exceptions. You can implement interfaces like IDbCommandInterceptor or IDbConnectionInterceptor.

Tip: Interceptors are powerful for cross-cutting concerns like auditing and performance monitoring.

5. Change Tracking Proxies

By default, EF Core uses proxies to enable change tracking. You can disable proxy creation if it's not needed, which can improve performance in some scenarios.

optionsBuilder.UseLazyLoadingProxies(false);

6. Concurrency Control

EF Core supports optimistic concurrency control using row versioning (e.g., a timestamp column) or by checking all columns during updates.

modelBuilder.Entity<Blog>()
    .Property(b => b.RowVersion)
    .IsConcurrencyToken();
Note: When a concurrency conflict occurs, EF Core throws a DbUpdateConcurrencyException. You'll need to handle this exception to resolve the conflict.

7. TPT and TPC Inheritance Mapping

EF Core supports different strategies for mapping table-per-type (TPT) and table-per-hierarchy (TPC) inheritance in the database.

  • TPT: Each concrete entity type is mapped to its own table.
  • TPC: All entity types in a hierarchy are mapped to a single table, with discriminator columns.

These strategies have implications for query performance and database schema complexity.

8. Batch Operations

While EF Core doesn't natively support batching of multiple statements into a single database roundtrip for *all* operations, there are community-driven solutions and techniques to achieve this for significant performance gains, especially for large inserts or updates.

9. Value Objects

Value objects are entities that don't have an identity of their own but are defined by their attributes. EF Core allows you to map value objects by owning them within other entities.

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

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address ShippingAddress { get; set; }
}

When mapping Value Objects, you can choose to map them as owned types, either in the same table as the owner or in separate tables.