Advanced Entity Framework Core Topics

Advanced Querying and Performance

Explore techniques to optimize query performance and handle complex data retrieval scenarios.

Raw SQL Queries

Sometimes, you need to execute raw SQL queries for performance or to access database-specific features not supported by LINQ.


var products = context.Products
    .FromSqlRaw("SELECT * FROM Products WHERE Category = {0}", "Electronics");

foreach (var product in products)
{
    Console.WriteLine(product.Name);
}
                

ExecuteUpdate and ExecuteDelete

Efficiently update or delete multiple rows in a single database command without loading entities into memory.


var updatedCount = await context.Products
    .Where(p => p.Price < 10.0m)
    .ExecuteUpdateAsync(p => p.SetProperty(p => p.Price, p => p.Price * 1.05m));

Console.WriteLine($"Updated {updatedCount} products.");
                

Batching Changes

EF Core automatically batches changes when calling SaveChanges, but understanding how it works can help in complex scenarios.

Connection Resiliency and Retries

Configure EF Core to automatically retry database operations in case of transient failures.


var options = new DbContextOptionsBuilder()
    .UseSqlServer("Server=.;Database=MyDatabase;Trusted_Connection=True;MultipleActiveResultSets=true",
        sqlServerOptions => sqlServerOptions.EnableRetryOnFailure(
            maxRetryCount: 5,
            maxRetryDelay: TimeSpan.FromSeconds(30),
            errorNumbersToAdd: null))
    .Options;
                

Mapping and Model Configuration

Go beyond basic conventions with advanced mapping strategies and model customization.

Owned Types

Map complex properties in your entities without a foreign key relationship, often used for address or contact information.


public class Order
{
    public int Id { get; set; }
    public ShippingAddress ShippingAddress { get; set; }
}

public class ShippingAddress
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

// In DbContext.OnModelCreating:
modelBuilder.Entity().OwnsOne(o => o.ShippingAddress);
                

TPH, TPT, and TPC Table Mapping

Understand different strategies for mapping inheritance hierarchies to database tables.

Strategy Description Database Structure
TPH (Table-Per-Hierarchy) A single table stores all entities, with discriminator columns. Single table with discriminator.
TPT (Table-Per-Type) Each concrete entity type maps to its own table. Separate tables for each entity, linked by foreign keys.
TPC (Table-Per-Concrete Type) Each concrete entity type maps to its own table, only storing columns specific to that type. Base type columns are duplicated. Separate tables for each concrete entity, base type columns duplicated.

Value Objects

Represent concepts that have value but no identity, like Money or DateRange.

Complex Type Mapping

Map properties of complex types to different columns within the same table.

Interceptors and Extension Methods

Hook into the EF Core pipeline to customize behavior.

SaveChangesInterceptor

Intercept the saving process to add auditing, validation, or modify entities before they are saved.


public class AuditInterceptor : DbCommandInterceptor
{
    public override InterceptionResult SavingChanges(
        DbContextEventData eventData,
        InterceptionResult result)
    {
        // Logic to audit changes
        return result;
    }
}

// In DbContextOptionsBuilder:
.AddInterceptors(new AuditInterceptor());
                

DbCommandInterceptor

Intercept database commands before they are executed.

Custom Provider Features

Leverage database-specific features through custom provider implementations or extension methods.

Other Advanced Topics

Concurrency Control

Handle concurrent updates to the same data using row versioning or optimistic concurrency.

Batching and Bulk Operations

For very large operations, consider specialized libraries for bulk inserts, updates, and deletes.

Transactions

Understand how EF Core manages transactions and how to control them explicitly.


using var transaction = await context.Database.BeginTransactionAsync();
try
{
    // Perform operations
    await context.SaveChangesAsync();
    await transaction.CommitAsync();
}
catch
{
    await transaction.RollbackAsync();
    throw;
}
                

Database-Specific Functionality

Utilize SQL Server's JSON functions, PostgreSQL's array types, or other provider-specific features.

Tip: Always measure performance before and after applying optimizations. Use tools like SQL Server Profiler or EF Core logging to identify bottlenecks.
EF Core's extensibility allows you to tailor its behavior to your specific application needs, offering powerful tools for data access optimization and customization.