Skip to content
Web and Mobile Development

Entity Framework interview questions and answers

80 questions, 19 with code. About 4 h 15 min to read them all.

By TestLab · Updated

Plan for an interview
80 questions

Track what you know and what to revise.

Sign in to track progress

You can open a few full answers without signing in. Sign in to read all

1

Describe the overall architecture and main components of Entity Framework Core.

Entity Framework Core is an object-relational mapper whose architecture centers on the DbContext, which acts as a session and unit of work over a database. It combines a metadata Model, a Change Tracker, a query pipeline that translates LINQ into SQL, an update pipeline that persists changes, and pluggable database providers.

Read full answer

Applies toEF Core 8 on .NET 8

Core Components

  • DbContext: represents a session with the database; owns a DbSet<T> per entity type, the change tracker, and the model. Registered as Scoped.

  • DbSet<T>: a collection-like entry point for querying and adding entities of type T.

  • Model (IModel): metadata describing entities, keys, relationships, and mappings, built at startup from OnModelCreating, data annotations, and conventions.

  • Database Provider: translates the common model into provider-specific SQL and behavior (for example, Microsoft.EntityFrameworkCore.SqlServer or Npgsql.EntityFrameworkCore.PostgreSQL).

Change Tracking

  • The change tracker snapshots entity state when loaded and detects modifications (Added, Modified, Deleted, Unchanged, Detached).

  • AsNoTracking skips tracking for read-only queries, reducing memory and CPU overhead.

  • Tracking queries return entities attached to the context; SaveChanges persists tracked changes.

Query Pipeline

  • LINQ queries are built as expression trees and translated to SQL by the provider’s query compiler.

  • Include/ThenInclude eager-load related data; projections (Select) avoid N+1 queries and over-fetching.

  • Queries use deferred execution and run when enumerated; results are materialized into entities or DTOs.

Update Pipeline

  • SaveChanges wraps changes in a transaction by default and issues INSERT/UPDATE/DELETE statements.

  • For bulk operations, ExecuteUpdate and ExecuteDelete (EF Core 7+) translate directly to SQL without loading entities.

  • Concurrency tokens and optimistic concurrency checks are evaluated during save.

Migrations

  • Migrations are code-based schema snapshots; dotnet ef migrations add <Name> generates them and dotnet ef database update applies them.

  • The __EFMigrationsHistory table tracks which migrations have been applied.

Best Practices and Pitfalls

  • Register DbContext as Scoped via AddDbContext; never share it across threads or keep it as a Singleton.

  • Use AsNoTracking for reads, projections to limit columns, and Include/ThenInclude to avoid N+1 queries.

  • Use ExecuteUpdate/ExecuteDelete for bulk changes instead of loading entities.

  • Parameterize raw SQL via FromSql/ExecuteSql with interpolated parameters; never concatenate user input into FromSqlRaw.

  • Dispose contexts through using or DI scopes; avoid long-lived contexts.

2

How does Entity Framework compare to LINQ to SQL in terms of features and supported platforms?

Entity Framework (specifically EF Core 8 on .NET 8) is a full-featured, cross-database object-relational mapper, while LINQ to SQL is a legacy, SQL Server-only data access technology from the .NET Framework era that is no longer actively developed.

Read full answer

Applies toEF Core 8 on .NET 8; LINQ to SQL on .NET Framework (legacy, no current release).

Provider and Platform Support

  • LINQ to SQL targets SQL Server (and SQL Server Compact historically) only; it has no provider model for other databases.

  • EF Core supports many databases through provider packages (SQL Server, PostgreSQL, MySQL, SQLite, Cosmos DB, and others) and runs on .NET 8 across Windows, Linux, and macOS.

  • LINQ to SQL is tied to .NET Framework and is not available for modern .NET; EF Core is the supported path for new development.

Feature Set

  • LINQ to SQL offers a lightweight DataContext, attribute- or XML-based mapping, basic change tracking, and limited inheritance mapping; it has no migration tooling.

  • EF Core provides DbContext, code-first and database-first workflows, migrations, rich change tracking, global query filters, interceptors, split queries, and bulk operations such as ExecuteUpdate/ExecuteDelete.

  • EF Core supports complex mappings (owned types, table splitting, many-to-many without join entities) that LINQ to SQL does not.

Lifecycle and Maintenance

  • LINQ to SQL is in maintenance mode within .NET Framework and receives no new features; Microsoft recommends migrating away from it.

  • EF6 (also .NET Framework) was the successor bridge, but EF Core is the current, actively developed ORM for .NET.

  • New projects should use EF Core; existing LINQ to SQL codebases are typically migrated to EF Core rather than extended.

Practical Guidance

  • Use EF Core for new applications, cross-platform targets, or non-SQL Server databases.

  • Keep LINQ to SQL only in legacy .NET Framework codebases where migration cost is not yet justified.

  • When migrating, map DataContext concepts to DbContext, replace SubmitChanges with SaveChanges, and adopt EF Core migrations instead of manual schema scripts.

3

What role does DbSet serve within the Entity Framework Core architecture?

In Entity Framework Core, DbSet represents a collection of entities within a DbContext that can be queried from the database and saved back to it. It acts as the primary gateway for performing CRUD operations on a specific entity type while translating LINQ expressions into SQL.

Read full answer

Applies toEF Core 8 on .NET 8

Core Functionality

  • Entity Collection: A DbSet<TEntity> corresponds logically to a database table and holds instances of the entity type TEntity.

  • LINQ Translation: Queries written against a DbSet using LINQ are converted into SQL commands executed against the underlying database provider.

  • Change Tracking: When entities are retrieved without AsNoTracking, the context tracks modifications so they can be persisted later via SaveChanges.

Relationship with DbContext

The DbContext acts as a session manager coordinating transactions and identity resolution, while DbSet properties expose the actual data collections to the developer. You define DbSet properties on your context class to make them accessible through dependency injection. During registration, the context is configured as scoped to ensure each request gets a fresh instance, preventing thread-safety issues and stale data across concurrent operations.

Querying and Saving Data

  • Reading: Use methods like Find, FirstOrDefault, or ToList to retrieve data from the set.

  • Writing: Use Add, Attach, Update, or Remove to stage changes within the current unit of work.

  • Bulk Operations: EF Core 7+ introduces ExecuteUpdate and ExecuteDelete on DbSet for efficient bulk changes without loading entities into memory first.

Best Practices

  • Scoped Lifetime: Always register DbContext as scoped in web applications; never singleton or transient.

  • Read-Only Optimization: Apply AsNoTracking() when data is only displayed to reduce memory overhead and improve performance.

  • Avoid N+1: Use Include or explicit projections (Select) when loading related data to prevent multiple round trips.

  • Parameterization: Avoid raw SQL concatenation; use FromSqlInterpolated if raw queries are necessary to prevent injection.

4

What is the purpose of database migrations in Entity Framework Core and how do they facilitate schema evolution without data loss?

Migrations in Entity Framework Core act as version control for database schemas, letting teams evolve a database incrementally to match changes in the C# model while preserving existing data by generating reversible, ordered migration steps rather than recreating the database.

Read full answer

Applies toEF Core 8 on .NET 8

How Migrations Work

  • When the model changes, EF Core diffs the current model against the last migration snapshot ({ContextName}ModelSnapshot.cs) and generates a new migration class inheriting from Migration.

  • Up: contains operations such as CreateTable, AddColumn, and CreateIndex that move the schema forward.

  • Down: contains the reverse operations, enabling rollback.

  • Migrations are additive and applied in chronological order; each migration represents one discrete schema version.

Creating and Applying Migrations

  • Use the .NET CLI with the Microsoft.EntityFrameworkCore.Design package:

  • dotnet ef migrations add AddProductNameColumn scaffolds a migration.

  • dotnet ef database update applies pending migrations.

  • dotnet ef migrations remove deletes the last unapplied migration.

  • The tooling needs access to a DbContext, either through the startup project or through an IDesignTimeDbContextFactory.

Migration History Tracking

  • EF Core creates and reads the __EFMigrationsHistory table, which stores every applied migration ID and the EF Core product version.

  • Before applying a migration, the runtime checks this table so the same migration is never executed twice, making repeated deployments safe across multiple application instances.

How Schema Evolution Preserves Data

  • Because migrations alter only the changed parts of the schema, existing tables, rows, indexes, and constraints remain untouched unless an operation explicitly modifies them.

  • Adding a column, index, or foreign key does not delete data. Changing a column type or splitting a column usually requires a data-preserving sequence: add a new column, copy or transform the data, then drop the old column.

  • EF Core warns about potentially destructive operations, such as dropping a column or table, so developers can review them and, when needed, write custom data-migration logic inside Up/Down using Sql or InsertData.

  • The model snapshot lets EF compute precise diffs instead of guessing the target state, reducing accidental data loss from incorrect assumptions.

Best Practices and Safety

  • Review generated migrations: automatic inference may miss constraints, indexes, or required data transformations.

  • Never rely on automatic drops for production data: use explicit data-copying steps when a column must be renamed, split, or type-converted.

  • Generate idempotent scripts for production: dotnet ef migrations script produces SQL that checks __EFMigrationsHistory, allowing DBA review and safe deployment.

  • Test against a production-like copy: apply migrations to a staging database before release.

  • Use a scoped DbContext: when migrations are triggered from application code, resolve DbContext within a DI scope; never share one instance across threads or register it as a Singleton.

5

Can you describe the code-first, database-first, and model-first development patterns in Entity Framework and explain how the framework facilitates each one?

Entity Framework supports three development paradigms, though Code First is the recommended and most fully featured approach in modern versions; Database First is supported through scaffolding, while Model First is a legacy EF6 capability not carried into EF Core.

Read full answer

Applies toEF Core 8 on .NET 8 (EF6 noted for legacy workflows)

Code First Workflow

In Code First, you define domain classes and a DbContext subclass manually. EF Core builds the model from these classes using data annotations or the Fluent API in OnModelCreating. You generate or update the database using migrations, created with dotnet ef migrations add and applied with dotnet ef database update, which track schema changes over time. This approach gives full control over the code and is ideal for greenfield projects where the application drives the schema design.

Database First Workflow

Database First starts with an existing database. You use the dotnet ef dbcontext scaffold command, or the Package Manager Console equivalent Scaffold-DbContext, to generate entity classes and a DbContext based on the current schema, together with the appropriate provider package such as Microsoft.EntityFrameworkCore.SqlServer. This is useful for brownfield projects or when the database is managed by a separate team. Generated code often uses partial classes, allowing you to extend logic without losing changes when rescaffolding after schema updates.

Model First Clarification

Model First traditionally involved designing a visual Entity Data Model (EDMX) diagram to generate code and database scripts. This workflow is not supported in EF Core; it is specific to EF6 on the .NET Framework. In EF Core, the conceptual model is defined by code (Code First) or reverse-engineered from a database (Database First). Teams migrating from EF6 must abandon EDMX files and adopt Code First patterns to remain on supported versions.

DbContext Lifecycle Management

Regardless of the approach, the DbContext must be registered with dependency injection as a scoped service using AddDbContext. Sharing a context across threads or registering it as a singleton can cause concurrency issues, stale data, and memory leaks. Proper scoping ensures one context instance per request in web applications, aligning with the unit-of-work pattern.

Pattern Comparison

Pattern Starting point EF Core support Best use case
Code First C# classes and DbContext Fully supported, recommended New applications, domain-driven design
Database First Existing database Supported via scaffolding Legacy databases, DBA-owned schemas
First EDMX designer EF6 only; unavailable in EF Core Maintaining older EF6 applications

Best Practices and Pitfalls

  • Use AsNoTracking for read-only queries to improve performance by disabling change tracking.

  • Prefer Include, ThenInclude, or projections to avoid N+1 query problems when loading related data.

  • Use ExecuteUpdate or ExecuteDelete (EF Core 7+) for bulk operations to avoid loading entities into memory.

  • Never concatenate user input into raw SQL; use parameterized queries via FromSqlInterpolated.

  • Treat generated Database First classes as partial to allow extending logic without losing changes during rescaffolding.

  • Avoid mixing approaches; choose one workflow per project to prevent model conflicts.

6

Can you describe the function of the EdmGen utility within Entity Framework and clarify its status in current development practices?

EdmGen is a legacy command-line tool from the .NET Framework era of Entity Framework (EF6 and earlier) that generates Entity Data Model (EDM) XML files and object-layer code from an existing database schema. It is not used in EF Core 8, which replaces EDMX-based mapping with code-first models and CLI scaffolding.

Read full answer

Applies toEntity Framework 6 (legacy) and EF Core 8 on .NET 8

Historical Role in EF6

In the Entity Framework 6 workflow, EdmGen.exe facilitated the Database First and Model First approaches. It operated on three main artifacts: - Conceptual Schema (CSDL): Defines the object model visible to the application. - Storage Schema (SSDL): Describes the database schema. - Mapping Specification (MSL): Maps between CSDL and SSDL. The tool could generate these XML files along with the corresponding C# entity classes and context class, allowing developers to work with strongly-typed objects without manually writing mapping code.

Why It Is Obsolete

EF Core abandoned the EDMX XML format entirely in favor of a code-first paradigm. The dependencies required by EdmGen, such as System.Data.Entity, do not exist in .NET Core or .NET 8. Consequently, attempting to use EdmGen with an EF Core project will fail because the underlying assembly infrastructure is missing. The shift was made to improve performance, reduce complexity, and enable cross-platform development.

Modern Equivalent in EF Core

The functionality provided by EdmGen is replaced by the dotnet ef CLI tool and the Scaffold-DbContext command in the Package Manager Console. These tools inspect the database schema and generate a DbContext class and entity POCOs directly in C#, rather than XML files. This generated code uses fluent API configurations or data annotations instead of MSL mappings.

Configuration Contrast

In EF Core, mapping is configured in code rather than separate XML files. A DbContext typically overrides OnModelCreating and uses the fluent API or data annotations to define entities, relationships, constraints, and column mappings. This keeps the model in ordinary C# code that is compiled with the application.

Best Practices for Interviews

When discussing legacy tools like EdmGen, always clarify the version context to avoid confusion. - Specify Versions: Explicitly state that EdmGen belongs to EF6/.NET Framework, not EF Core. - Recommend Modern Tooling: Advise using dotnet ef dbcontext scaffold for new projects. - Avoid EDMX: Do not suggest XML-based mapping for new .NET 8 applications, as it is unsupported. - Security: Ensure connection strings in generated code are managed via user secrets or environment variables, not hardcoded as shown in minimal examples.

7

What is the Entity Data Model within Entity Framework and how does it map application classes to database structures?

The Entity Data Model (EDM) serves as the metadata abstraction that maps .NET objects to relational database structures. In modern development using EF Core 8, this model is constructed at runtime through code rather than static XML files, defining entities, properties, keys, and relationships explicitly within the DbContext.

Read full answer

Applies toEF Core 8 on .NET 8

Core Model Components

The model consists of metadata describing how application classes correspond to database schema elements. - Entities: C# classes that map to database tables, represented as DbSet<T> properties in the context. - Properties: Class members that map to table columns, including primitive types and complex owned types. - Keys: Primary keys uniquely identify records, while foreign keys establish relationships between tables. - Relationships: Associations such as one-to-many or many-to-many are configured to generate correct join constraints.

Configuration Strategies

EF Core 8 builds the model using three layered approaches applied during context initialization. - Conventions: Default rules automatically configure the model based on naming patterns, such as treating a property named Id as the primary key. - Data Annotations: Attributes like [Key] or [Required] placed directly on class properties override conventions. - Fluent API: The OnModelCreating method provides the most granular control, allowing complex configurations without cluttering domain classes.

Legacy EDM vs Modern Model

Historically, EF6 used an Entity Data Model defined by EDMX files; EF Core replaces this with a code-first runtime model.

Aspect EF6 (EDMX) EF Core (Code-First)
Model definition XML files (CSDL, SSDL, MSL) C# classes and OnModelCreating
Design-time tooling Visual designer or manual XML editing Fluent API and data annotations
Runtime metadata MetadataWorkspace IModel built when the context is created
Migration path N/A Convert EDMX mappings into Fluent API configurations or annotations

Best Practices and Registration

To ensure stability and performance, register the context correctly and optimize model usage. - Scoped Lifetime: Always register the DbContext as scoped using services.AddDbContext<AppDbContext>() in dependency injection; never use Singleton or transient scopes. - Read-Only Queries: When querying data without intent to modify, use AsNoTracking to reduce memory overhead and improve speed. - Configuration Location: Prefer Fluent API in OnModelCreating for infrastructure rules to keep domain models clean of persistence concerns. - Avoid Mixed Signals: Do not contradict data annotations with Fluent API configurations, as this can lead to runtime model building exceptions.

8

How does the lazy loading feature operate within Entity Framework Core, and what prerequisites are necessary for its execution?

Lazy loading in Entity Framework Core automatically retrieves related data from the database when a navigation property is accessed for the first time, rather than loading it upfront with the parent entity. This behavior relies on runtime-generated proxy classes that intercept property access to issue additional SQL queries transparently.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

Lazy loading is implemented via dynamic proxies. When you request an entity, EF Core returns an instance of a proxy class derived from your entity class instead of the entity itself. This proxy overrides virtual navigation properties to check if the related data is loaded. If not, it uses the context’s current connection to execute a query and fetch the related data before returning the value. This requires: - Virtual Properties: Navigation properties must be marked virtual to allow proxy override. - Proxy Package: The Microsoft.EntityFrameworkCore.Proxies package must be installed. - Tracking Queries: The entity must be tracked by the context; lazy loading does not work with AsNoTracking.

Configuration Steps

To enable lazy loading, you must configure the DbContext options during registration. Since DbContext instances should never be singletons or shared across threads, register them as Scoped in your dependency injection container.

  1. Install the Microsoft.EntityFrameworkCore.Proxies NuGet package.

  2. Call UseLazyLoadingProxies() within OnConfiguring or when adding the context.

  3. Ensure all navigation properties intended for lazy loading are declared as virtual.

Lazy Loading vs. Eager Loading

Choosing between loading strategies impacts performance significantly. Eager loading uses Include to fetch data in a single join query, while lazy loading issues separate queries on demand.

Feature Lazy Loading Eager Loading (Include)
Query Count 1 + N (risk of N+1) 1 (usually)
Data Loaded Only when accessed Immediately
Context Lifetime Must remain open/disposable Can be detached sooner
Configuration Requires proxies package Built-in

Common Pitfalls

The most significant risk is the N+1 query problem. If you iterate over a collection of parents and access a child navigation property for each, EF Core issues one query for the parents and one query per parent for the children. This degrades performance drastically compared to a single join query. Additionally, accessing lazy-loaded properties after the DbContext is disposed will throw an ObjectDisposedException. This reinforces why the DbContext scope must encompass the entire unit of work where data is accessed.

Best Practices

  • Avoid in Loops: Never access navigation properties inside a loop over a query result; use Include instead.

  • Scoped Lifetime: Always register DbContext as Scoped to ensure it lives long enough for lazy loading to trigger but not long enough to cause concurrency issues.

  • Explicit Loading Alternative: If proxies are undesirable, use Load() or LoadAsync() for explicit control over when related data is fetched.

  • Read-Only Scenarios: Use AsNoTracking for read-only operations, but remember this disables lazy loading, so plan your data retrieval strategy accordingly.

10

In Entity Framework Core, how does configuration applied to a specific entity differ from configuration applied across the entire model?

In Entity Framework Core, local configuration targets one entity type or its members, while global configuration defines conventions that EF applies across the model or to matching types during model building. This distinction controls which mapping rules are specific exceptions and which are shared standards.

Read full answer

Applies toEF Core 8 on .NET 8

Local Configuration Scope

Local configuration targets individual entity types or specific properties within them. It is typically implemented with the Fluent API inside OnModelCreating or with Data Annotations on the entity class. - Fluent API: modelBuilder.Entity<Blog>() configures keys, relationships, table names, or column names for Blog only. - Data Annotations: Attributes such as [Key] or [MaxLength] are attached directly to the class or property. - Use Case: Best for exceptions to model-wide rules, such as a special table name or a unique relationship. - Override Behavior: Explicit local settings override broader conventions for the targeted entity.

Global Configuration Scope

Global configuration affects all entities in the model or all entities matching a convention. It is defined once and propagates automatically, reducing repetitive code. - Conventions: ConfigureConventions sets default behaviors, such as default column types or length limits for matching property types. - Scope: Applies model-wide or to entities matching the configured convention. - Use Case: Suitable for cross-cutting standards such as consistent data types or standardized mapping defaults. - Efficiency: Reduces boilerplate when many entities share the same mapping requirements.

Query Filters Are Entity-Level

HasQueryFilter is not model-wide configuration. It is configured for a specific entity type, but it applies to every query against that entity. It is commonly used for soft deletes or multi-tenancy, and it must be configured separately for each entity that needs it.

Comparison of Precedence and Scope

Feature Local Configuration Global Configuration
Scope Single entity type All entities or matching types
Definition Entity<T>() or attributes ConfigureConventions()
Precedence Higher for the targeted entity Lower; applied as conventions
Maintenance More effort per entity Centralized management

When both exist, explicit local settings override global conventions for the specified entity. Global conventions are applied during model discovery and building, while local Fluent API calls refine the resulting model.

Best Practices

  • Prefer Global for Standards: Use global conventions for consistent standards such as default data types or mapping defaults.

  • Use Local for Exceptions: Reserve local configuration for entities that deviate from model-wide conventions.

  • Keep Domain Models Clean: Prefer Fluent API over Data Annotations for complex configuration so domain classes remain testable.

  • Register DbContext Correctly: Register the DbContext as Scoped with AddDbContext; do not share one instance across threads or keep it as a Singleton.

  • Configure Filters Per Entity: Apply soft-delete or tenant filters with HasQueryFilter on each relevant entity rather than treating them as model-wide conventions.

  • Review the Built Model: Inspect the generated model during development to confirm that conventions do not unintentionally override specific business requirements.

11

How do you manage table name pluralization and singularization conventions in Entity Framework Core?

Entity Framework Core has no built-in pluralization or singularization convention, so table names are controlled through manual table-name configuration in OnModelCreating or with per-entity ToTable calls.

Read full answer

Applies toEF Core 8 on .NET 8

Default Behavior

EF6 included PluralizingTableNameConvention, which mapped an entity such as Blog to a table named Blogs. EF Core removed that convention, so Blog maps to Blog by default. If your schema uses plural or singular table names, configure that mapping explicitly.

Global Convention in OnModelCreating

You can iterate over modelBuilder.Model.GetEntityTypes() and set each table name. This is useful when you want one naming rule across the model. For accurate English pluralization and singularization, use a library such as Humanizer rather than simple string manipulation.

Per-Entity Configuration

For a small model, configure each entity directly:

modelBuilder.Entity<Blog>().ToTable("Blogs");

Code Example

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

public class AppDbContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            var currentName = entityType.GetTableName();

            if (!string.IsNullOrEmpty(currentName))
            {
                // Example only: pluralize a simple noun.
                // Use Humanizer for production pluralization/singularization rules.
                var pluralName = currentName.EndsWith("s", StringComparison.Ordinal)
                    ? currentName
                    : currentName + "s";

                entityType.SetTableName(pluralName);
            }
        }
    }
}

Best Practices and Pitfalls

  • Consistency: Match the configured names to your existing database schema before creating migrations.

  • Irregular nouns: Avoid naive suffix rules for words such as Person, Child, or Address; use Humanizer or explicit ToTable calls.

  • Singular convention: If your schema uses singular table names, leave EF Core’s default behavior or explicitly set singular names.

  • Migrations: Renaming tables after migrations exist requires a migration that renames the tables safely.

  • Registration: Register the DbContext with AddDbContext so it is scoped per request.

  • Read-only queries: Use AsNoTracking when querying data that will not be updated.

12

What are the standard patterns for implementing create, read, update, and delete data operations with Entity Framework Core?

Performing CRUD operations in Entity Framework Core revolves around the DbContext class, which acts as a unit of work and repository for tracking entity changes. Entities are queried and modified through DbSet<T> properties, and all changes are persisted by calling SaveChangesAsync.

Read full answer

Applies toEF Core 8 on .NET 8

DbContext Lifecycle and Registration

Register the DbContext as a scoped service with AddDbContext in Program.cs, for example builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString)). This gives one instance per request and avoids the concurrency and memory problems that arise from Singleton or Transient lifetimes. Never share a DbContext across threads.

Reading Data Efficiently

Use AsNoTracking for read-only queries so EF Core does not spend memory tracking unchanged entities. Prevent N+1 query problems by loading related data with Include and ThenInclude, or by projecting only the required fields with Select. Projections to DTOs are usually the best choice for read-only screens.

Creating, Updating, and Deleting Records

For a single entity, call Add, Update, or Remove on the appropriate DbSet<T> and then SaveChangesAsync. For bulk changes where loading entities would be wasteful, EF Core 7+ provides ExecuteUpdate and ExecuteDelete; these translate directly to SQL and run without materializing entities.

Code Example

using Microsoft.EntityFrameworkCore;
using System.Threading;
using System.Threading.Tasks;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public Category? Category { get; set; }
}

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class AppDbContext : DbContext
{
    public DbSet<Product> Products => Set<Product>();

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}

public class ProductService
{
    private readonly AppDbContext _context;

    public ProductService(AppDbContext context) => _context = context;

    public async Task<Product?> GetByIdAsync(int id, CancellationToken ct)
    {
        return await _context.Products
            .AsNoTracking()
            .Include(p => p.Category)
            .FirstOrDefaultAsync(p => p.Id == id, ct);
    }

    public async Task AddAsync(Product product, CancellationToken ct)
    {
        await _context.Products.AddAsync(product, ct);
        await _context.SaveChangesAsync(ct);
    }

    public async Task UpdateAsync(Product product, CancellationToken ct)
    {
        _context.Products.Update(product);
        await _context.SaveChangesAsync(ct);
    }

    public async Task UpdatePriceAsync(int id, decimal price, CancellationToken ct)
    {
        await _context.Products
            .Where(p => p.Id == id)
            .ExecuteUpdateAsync(s => s.SetProperty(p => p.Price, price), ct);
    }

    public async Task DeleteAsync(int id, CancellationToken ct)
    {
        await _context.Products
            .Where(p => p.Id == id)
            .ExecuteDeleteAsync(ct);
    }
}

Best Practices and Common Pitfalls

  • Scoped Lifetime: Always register DbContext as Scoped via AddDbContext; never keep it as a Singleton or pass it between threads.

  • Async I/O: Use async methods such as ToListAsync, FirstOrDefaultAsync, and SaveChangesAsync to free threads during database operations.

  • No Tracking: Apply AsNoTracking to queries that will not be saved.

  • Avoid N+1: Use Include/ThenInclude or projections; prefer projections when the UI needs only a subset of columns.

  • Bulk Writes: Use ExecuteUpdateAsync/ExecuteDeleteAsync (EF Core 7+) for mass changes instead of loading and saving many entities.

  • Raw SQL: Never concatenate user input into FromSqlRaw or ExecuteSqlRaw; use FromSqlInterpolated and ExecuteSqlInterpolated so parameters are sent safely.

  • Cross-Request Entities: Do not pass tracked entities between requests. If an entity originates from another context, reattach it with Update or Attach instead of relying on stale tracking.

13

Which APIs and patterns does Entity Framework Core offer for querying and retrieving data from the database?

Entity Framework Core provides several mechanisms for reading data, primarily through LINQ queries that are translated into SQL, alongside support for raw SQL execution. The most essential distinction is between tracking queries, which monitor changes for persistence, and non-tracking queries, optimized for read-only scenarios. Selecting the appropriate method depends on whether the data will be modified and how related entities should be loaded.

Read full answer

Applies toEF Core 8 on .NET 8

LINQ Query Execution

Standard data retrieval uses LINQ methods on DbSet<T> properties. These queries are executed against the database when enumeration occurs. - Tracking Queries: Default behavior; entities are tracked by the DbContext. Suitable when updates are expected. - Non-Tracking Queries: Append AsNoTracking() to reduce memory overhead and improve performance for read-only operations. AsNoTrackingWithIdentityResolution() is available when you need identity resolution without change tracking. - Termination Methods: Queries run when calling ToList(), ToArray(), First(), SingleOrDefault(), Count(), or Any(). - Split Queries: Use AsSplitQuery() to load related collections in separate SQL statements, preventing cartesian explosion in complex graphs.

Loading Related Data

Retrieving connected entities efficiently is critical to avoid performance pitfalls. - Eager Loading: Use Include and ThenInclude to fetch related data in a single database round trip. - Projections: Use Select to retrieve only specific columns or DTOs, which minimizes data transfer and avoids N+1 query issues. - Explicit Loading: Use Entry(entity).Collection(e => e.Related).Load() or Entry(entity).Reference(e => e.Related).Load() to load related data on demand; be aware that each call issues a separate query. - N+1 Prevention: Always verify that related data is loaded within the same query scope when iterating over parent entities.

Raw SQL Queries

When LINQ cannot express a complex query, EF Core allows raw SQL execution while maintaining safety. - FromSqlInterpolated: Use this method to pass parameters safely via string interpolation. EF Core converts parameters to SQL parameters automatically. - Security Warning: Never concatenate user input into FromSqlRaw, as this creates SQL injection vulnerabilities. Always prefer interpolated strings or explicit parameter objects. - Composability: Raw SQL queries can be composed with LINQ operators like Where or OrderBy after the FromSqlInterpolated call.

Best Practices and Configuration

  • DbContext Lifetime: Register the context as Scoped using AddDbContext in dependency injection. Never share a context across threads or use Singleton lifetime.

  • Performance: Prefer projections over loading full entities when only displaying data. Use AsNoTracking for all read-only endpoints.

  • Bulk Reads: For large datasets, consider chunking results or using keyset pagination instead of Skip/Take on deep pages.

  • Monitoring: Enable logging to inspect generated SQL and verify that includes are working as intended without causing cartesian products.

14

In Entity Framework Core, what is an entity and what are the standard ways to define one within your data model?

In Entity Framework Core, an entity is a plain class that represents a row in a database table, where each instance corresponds to a specific record and each property maps to a column. You define them primarily by creating Plain Old CLR Object (POCO) classes with public properties, then configuring their mapping to the database schema using conventions, data attributes, or the Fluent API within a DbContext.

Read full answer

Applies toEF Core 8 on .NET 8

Core Characteristics of an Entity

  • Definition: An entity is a plain class that represents a row in a database table; each instance corresponds to a record and each property maps to a column.

  • Instantiation: EF Core can materialize entity classes that have a public parameterless constructor; the class may be abstract when used as a base type in an inheritance hierarchy (TPH, TPT, or TPC), but concrete derived types must be instantiable.

  • Change tracking: Properties are typically public read-write so the DbContext can detect changes; read-only properties can be mapped via backing fields.

  • Persistence: Attached entities are tracked, and INSERT/UPDATE/DELETE commands are generated automatically when SaveChanges is called.

Approaches to Definition and Mapping

  • Conventions: Applied automatically; for example, a property named Id or ClassNameId becomes the primary key, and a class name becomes the table name.

  • Data annotations: Attributes such as [Key], [Column], [MaxLength] add metadata directly to the class; simple but couples the model to EF Core.

  • Fluent API: Configured in DbContext.OnModelCreating using ModelBuilder; offers the fullest control without touching the entity class, preferred for complex mappings and domain-driven designs.

Primary Keys and Navigation Properties

  • Primary key: Every entity must have a key, configured by convention, annotation ([Key]), or Fluent API (HasKey).

  • Navigation properties: Represent relationships—reference types for one-to-one or many-to-one, ICollection<T> for one-to-many or many-to-many.

  • Owned entities and complex types: Owned entities (value objects embedded in the parent table) have been supported since EF Core 2.0; EF Core 8 introduces complex types as a lighter-weight alternative for value objects that do not require full entity semantics.

Best Practices

  • Separation of concerns: Keep entities focused on persistence; avoid embedding business logic that belongs in domain services.

  • DbContext lifetime: Register the DbContext as scoped (AddDbContext) in dependency injection; never share it across threads or use it as a singleton.

  • Read-only queries: Use AsNoTracking when entities are not going to be modified, to reduce change-tracking overhead.

  • Expose DTOs, not entities: Return Data Transfer Objects from APIs to prevent over-posting and to decouple the contract from the schema.

15

What mechanism does Entity Framework Core use to persist classes that do not define a primary key, and how has this feature evolved in recent releases?

Entity Framework Core persists classes that have no primary key through Complex Types, a mapping feature introduced in EF Core 8 that embeds a value object’s columns directly into the owner entity’s table. Earlier versions modeled the same shape with owned types or EF6 complex types, but EF Core 8 gives value objects a dedicated configuration without ownership semantics.

Read full answer

Applies toEF Core 8 on .NET 8 (owned types remain available from EF Core 2.0+)

Evolution of Keyless Mapping

  • EF6: value-object classes were mapped with ComplexType, storing their columns in the owner’s table without identity or independent querying.

  • EF Core 2.0–7: there was no dedicated complex type; the same pattern was modeled with Owned Types through OwnsOne or OwnsMany, which carried ownership semantics such as cascade delete and optional separate-table mapping.

  • EF Core 8+: reintroduces the concept explicitly as Complex Types, configured with ComplexProperty, for value objects that are always part of a parent and have no identity.

Version Mechanism Configuration Default storage
EF6 Complex type ComplexType() or data annotation Same table as owner
EF Core 2.0–7 Owned type OwnsOne / OwnsMany Same table by default, can be moved to a separate table
EF Core 8+ Complex type ComplexProperty Always same table as owner

Configuration and Mapping

  • Define a plain C# class with no key property and expose it as a property on the entity.

  • In OnModelCreating, configure it with modelBuilder.Entity<Owner>().ComplexProperty(e => e.ValueObject).

  • By default, each complex-type property becomes a column in the owner’s table using its own property name, such as Street and City. If two complex properties contain overlapping property names, rename columns with HasColumnName to avoid collisions.

  • The configuration action can also mark columns as required, apply value conversions, and support nested complex types.

  • A complex type cannot have its own DbSet, primary key, inverse navigation, or independent lifecycle.

Tracking and Querying

  • Changes to complex-type properties are tracked as part of the parent entity and are persisted in the same INSERT or UPDATE.

  • For read-only queries, use AsNoTracking to avoid change-tracking snapshot overhead.

  • Filter or project through the parent, for example context.Blogs.Where(b => b.Address.City == city); this translates to ordinary column comparisons.

  • Complex types cannot be queried independently, loaded lazily, or tracked separately from their owner.

Best Practices and Pitfalls

  • Use for value objects: choose complex types for structures like Address, Money, or Coordinates that lack identity and cannot exist without their owner.

  • Avoid shared instances: do not reuse the same complex-type instance across multiple entities, because EF Core treats it as part of the parent that owns it.

  • Nullability: make the complex-type property nullable only when the underlying columns should allow nulls; otherwise configure required columns explicitly.

  • Performance: because all data resides in the owner’s table, complex types avoid joins and the table-splitting ambiguity that owned types can introduce when mapped to a separate table.

  • DbContext lifetime: register the DbContext as scoped with AddDbContext; never share one instance across threads or store it as a singleton.

16

How do navigation properties work in Entity Framework Core?

Navigation properties are properties on an entity class that reference related entities, enabling EF Core to traverse one-to-one, one-to-many, and many-to-many relationships and materialize object graphs without manual SQL joins.

Read full answer

Applies toEF Core 8 on .NET 8

How They Work

  • A navigation property is not stored in a database column; it represents a relationship that EF Core resolves through a foreign key, which may be a real property or a shadow property.

  • When EF Core materializes query results, it populates navigations from joined rows, separate queries, or previously tracked entities.

  • Reference navigation: points to a single related entity, such as Order.Customer.

  • Collection navigation: points to a set of related entities, such as Customer.Orders.

  • Paired navigations are kept consistent by relationship fix-up: assigning one side in a tracked context automatically updates the inverse side.

Relationship Types

  • One-to-many: the principal entity holds a collection navigation; the dependent entity holds a reference navigation and the foreign key.

  • One-to-one: both sides hold reference navigations; the dependent side usually owns the foreign key, and requiredness helps determine principal/dependent assignment.

  • Many-to-many: both sides hold collection navigations. EF Core 8 creates the join table automatically (a skip navigation), but you can still map an explicit join entity when the join table needs extra columns.

  • Required versus optional: a non-nullable foreign key makes the relationship required; a nullable foreign key makes it optional, which affects cascade delete and whether the navigation can be null.

Loading Strategies

Strategy Mechanism Best used when
Eager loading Include and ThenInclude in the query the full graph is needed immediately
Explicit loading Entry(entity).Collection(...).Load() or Reference(...).Load() loading is conditional and proxies are disabled
Lazy loading virtual navigations plus the Microsoft.EntityFrameworkCore.Proxies package related data is needed only on demand
Projections Select into a DTO read-only scenarios where full entities are unnecessary

Key Points

  • Navigation properties are optional: EF Core can map and query relationships using foreign keys alone, but navigations make traversal and eager loading convenient.

  • Lazy loading requires the navigation to be virtual and the Microsoft.EntityFrameworkCore.Proxies package to be configured; otherwise an unloaded reference navigation returns null, and an unloaded collection navigation returns null unless it has been initialized.

  • Use AsNoTracking() for read-only queries to avoid tracking overhead; navigations still load normally with Include.

  • Avoid N+1 queries by loading required navigations up front with Include/ThenInclude or by projecting, rather than accessing navigations inside loops.

  • For bulk changes, prefer ExecuteUpdate/ExecuteDelete (EF Core 7+) instead of loading entities and modifying navigation collections.

Best Practices and Common Pitfalls

  • Initialize collection navigations, for example public List<Order> Orders { get; set; } = new();, to prevent null-reference exceptions before data is loaded.

  • Keep navigation properties free of business logic; they are relationship accessors, not domain methods.

  • Register the DbContext as scoped with AddDbContext; never share one instance across threads or register it as a singleton.

  • Mark navigations virtual only when using lazy-loading proxies, because virtual members carry a small runtime cost and can confuse serialization.

  • Prefer projections over entity graphs for read-only APIs, and use AsNoTracking to reduce memory and change-tracking overhead.

17

How are entity keys defined and used in Entity Framework Core?

An entity key (primary key) is the property or set of properties that uniquely identifies each instance of an entity in EF Core; it underpins change tracking, identity resolution, and relationships between entities.

Read full answer

Applies toEF Core 8 on .NET 8

What Entity Keys Are

  • Every tracked entity type must have a key unless it is explicitly configured as keyless.

  • The key can be a single property or a composite of multiple properties.

  • EF Core uses the key to distinguish instances in the change tracker and to generate foreign-key relationships.

Defining Keys

  • Convention: a property named Id or <TypeName>Id (for example OrderId) is automatically treated as the primary key.

  • Data annotations: apply [Key] to a single property. Composite keys cannot be defined with data annotations and must use the Fluent API.

  • Fluent API: call HasKey in OnModelCreating, which is the recommended approach for composite keys and for keeping configuration out of domain classes.

Key Types and Value Generation

  • Integer keys are typically configured with ValueGeneratedOnAdd so the database generates the value on insert.

  • GUID keys can be generated client-side or by the database depending on the provider.

  • String and other non-numeric keys usually require an explicitly supplied value.

  • Alternate keys can be defined with HasAlternateKey when a non-primary unique column is needed for relationships.

Using Keys in Queries and Relationships

  • Find and FindAsync look up an entity by its primary key and check the change tracker first.

  • For read-only lookups by key without tracking, use AsNoTracking() with a query such as FirstOrDefaultAsync instead of FindAsync.

  • Foreign keys in related entities reference the principal’s key, enabling Include/ThenInclude navigation.

  • Keyless entity types (for views or raw SQL results) are configured with HasNoKey and cannot be tracked for updates.

Code Example

using Microsoft.EntityFrameworkCore;

public class Order
{
    public int Id { get; set; }                 // convention key
    public string CustomerName { get; set; } = string.Empty;
}

public class OrderItem
{
    public int OrderId { get; set; }
    public int ProductId { get; set; }
    public int Quantity { get; set; }
}

public class AppDbContext : DbContext
{
    public DbSet<Order> Orders { get; set; } = null!;
    public DbSet<OrderItem> OrderItems { get; set; } = null!;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Composite key via Fluent API
        modelBuilder.Entity<OrderItem>()
            .HasKey(oi => new { oi.OrderId, oi.ProductId });

        // Explicit value generation for the integer key
        modelBuilder.Entity<Order>()
            .Property(o => o.Id)
            .ValueGeneratedOnAdd();
    }
}

Best Practices and Common Pitfalls

  • Prefer immutable keys; changing a key value after an entity is tracked causes confusing change-tracker behavior.

  • Use HasKey in OnModelCreating rather than data annotations when keys are composite or provider-specific.

  • Avoid sharing a DbContext across threads; register it as scoped with AddDbContext.

  • For read-only lookups by key, use AsNoTracking() with FirstOrDefaultAsync rather than FindAsync, because FindAsync tracks the returned entity.

18

How do you configure relationships between entities in Entity Framework Core?

Associations in EF Core are mapped through navigation properties and foreign keys, discovered by convention and refined with data annotations or the Fluent API.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

EF Core infers a relationship when an entity exposes a navigation property to another entity together with a matching foreign key property. You can override convention-based discovery with data annotations such as [ForeignKey] and [InverseProperty], or with the Fluent API inside OnModelCreating, which is the recommended approach for anything beyond trivial cases.

Relationship Types

  • One-to-many: the most common shape; the dependent side carries the foreign key.

  • One-to-one: both sides have navigation properties, and one side owns the foreign key; configure with HasOne(...).WithOne(...).

  • Many-to-many: EF Core 5 and later create an implicit join entity automatically; use UsingEntity when you need to add payload columns to the join table.

Configuration Approaches

Approach When to use
Convention Simple, standard naming (BlogId, Blog)
Data annotations Small overrides on the entity class
Fluent API Complex rules, delete behavior, join entity payload, schema control

Avoiding N+1 Queries

Load related data deliberately with Include/ThenInclude, or project only the columns you need with Select. For read-only screens, add AsNoTracking() to avoid change-tracking overhead.

Code Example

using Microsoft.EntityFrameworkCore;

public class Blog
{
    public int Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public List<Post> Posts { get; set; } = new();
}

public class Post
{
    public int Id { get; set; }
    public string Content { get; set; } = string.Empty;
    public int BlogId { get; set; }
    public Blog Blog { get; set; } = null!;
    public List<Tag> Tags { get; set; } = new();
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public List<Post> Posts { get; set; } = new();
}

public class AppDbContext : DbContext
{
    public DbSet<Blog> Blogs => Set<Blog>();
    public DbSet<Post> Posts => Set<Post>();
    public DbSet<Tag> Tags => Set<Tag>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Post>()
            .HasMany(p => p.Tags)
            .WithMany(t => t.Posts);
    }
}

Best Practices and Common Pitfalls

  • Register the DbContext as scoped with AddDbContext; never share one across threads or keep it as a singleton.

  • Set OnDelete deliberately; cascading deletes can remove more data than intended.

  • Prefer explicit Fluent configuration for non-trivial relationships rather than relying on naming conventions.

  • Use AsNoTracking for read-only queries and projections to avoid N+1 problems.

  • For bulk updates or deletes, use ExecuteUpdate/ExecuteDelete (EF Core 7+) instead of loading entities into memory.

19

How do table per hierarchy and table per type inheritance mapping strategies differ in Entity Framework Core?

Entity Framework Core implements inheritance mapping where Table Per Hierarchy (TPH) consolidates all types into a single table distinguished by a discriminator column, whereas Table Per Type (TPT) assigns a separate table to each class linked by foreign keys. TPH is the default approach favoring query speed, while TPT prioritizes schema normalization at the expense of join complexity.

Read full answer

Applies toEF Core 8 on .NET 8

Table Per Hierarchy (TPH)

In TPH, the entire inheritance hierarchy maps to one database table. A discriminator column identifies the concrete type of each row. Properties unique to derived classes become nullable columns because they are unused by base type instances. This strategy is the EF Core default and requires no configuration. It enables fast reads since retrieving a derived entity does not require table joins, but it may waste storage space if derived properties are numerous.

Table Per Type (TPT)

TPT maps every class in the hierarchy to its own dedicated table. The base table holds shared properties, and derived tables hold specific properties connected via foreign keys to the base row. This design eliminates nullable columns and adheres to normalization principles. However, querying a derived type necessitates joining the base and derived tables, which increases database load. Developers must explicitly enable TPT in the model configuration.

Configuration in EF Core 8

TPH is used by default. To use TPT, call UseTptMappingStrategy() on the base entity in OnModelCreating. This changes the generated schema from one shared table to one table per type while preserving the inheritance relationship through foreign keys.

Performance and Storage Trade-offs

Aspect TPH TPT
Schema Structure Single wide table Multiple normalized tables
Read Performance High (no joins) Moderate (requires joins)
Write Performance Single insert Multiple inserts
Null Handling Nullable columns for derived props No nulls for specific props
Use Case Few derived properties Many distinct derived properties

Select TPH when minimizing query latency is the priority. Select TPT when strict schema normalization or non-nullable constraints on derived properties are required.

Best Practices

  • Register DbContext as Scoped: Always register DbContext via AddDbContext in dependency injection to ensure proper lifetime management and thread safety.

  • Use AsNoTracking: Apply AsNoTracking to inheritance queries that do not modify data to reduce change tracking overhead.

  • Plan Migrations Early: Switching between TPH and TPT after data generation requires complex data migrations; decide the strategy during design.

  • Avoid Deep Hierarchies: Deep inheritance trees complicate mapping; consider composition or interfaces if the hierarchy exceeds two levels.

  • Index Discriminators: For TPH, ensure the discriminator column is indexed if filtering by type is common.

20

How do you configure and query one-to-one, one-to-many, and many-to-many relationships in Entity Framework Core?

EF Core discovers relationships through navigation properties and foreign keys by convention, and you can configure cardinality, delete behavior, and join tables explicitly with the Fluent API.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

EF Core infers relationships from navigation properties on entity classes. A reference navigation property implies a many-to-one or one-to-one relationship, while a collection navigation property implies a one-to-many or many-to-many relationship. Foreign keys are created automatically when a property follows the <NavigationPropertyName>Id or <EntityName>Id naming convention. When conventions are insufficient, use OnModelCreating with the Fluent API to set cardinality, delete behavior, and join table details.

Configuring Relationships

  • One-to-one: each side has at most one counterpart; the dependent side needs a unique foreign key. Configure it with HasOne(...).WithOne(...).HasForeignKey(...).

  • One-to-many: one principal has many dependents. EF Core discovers it from a collection navigation property plus a reference and foreign key; use HasMany(...).WithOne(...) and set OnDelete to Cascade, Restrict, or SetNull.

  • Many-to-many: EF Core 5+ supports this directly through collection navigation properties on both sides and creates a join table automatically. Customize the join table with UsingEntity.

Querying Relationships

Use Include and ThenInclude to eagerly load related data and avoid N+1 queries. Add AsNoTracking() for read-only scenarios. For bulk changes, prefer ExecuteUpdate and ExecuteDelete (EF Core 7+) instead of loading entities into memory.

Code Example

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
    public AuthorProfile? Profile { get; set; }          // one-to-one
    public ICollection<Book> Books { get; set; } = new List<Book>(); // one-to-many
}

public class AuthorProfile
{
    public int Id { get; set; }
    public string Bio { get; set; } = default!;
    public int AuthorId { get; set; }
    public Author Author { get; set; } = default!;
}

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; } = default!;
    public int AuthorId { get; set; }
    public Author Author { get; set; } = default!;
    public ICollection<Tag> Tags { get; set; } = new List<Tag>(); // many-to-many
}

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
    public ICollection<Book> Books { get; set; } = new List<Book>();
}

public class AppDbContext : DbContext
{
    public DbSet<Author> Authors { get; set; } = default!;
    public DbSet<Book> Books { get; set; } = default!;
    public DbSet<Tag> Tags { get; set; } = default!;

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Author>()
            .HasOne(a => a.Profile)
            .WithOne(p => p.Author)
            .HasForeignKey<AuthorProfile>(p => p.AuthorId);

        modelBuilder.Entity<Book>()
            .HasMany(b => b.Tags)
            .WithMany(t => t.Books)
            .UsingEntity(j => j.ToTable("BookTags"));
    }
}

public async Task<List<Author>> GetAuthorsWithBooksAsync(AppDbContext db)
{
    return await db.Authors
        .AsNoTracking()
        .Include(a => a.Profile)
        .Include(a => a.Books)
            .ThenInclude(b => b.Tags)
        .ToListAsync();
}

Best Practices

  • Register the DbContext with AddDbContext using Scoped lifetime; never share it across threads or register it as a Singleton.

  • Use AsNoTracking() for read-only queries.

  • Prefer projections with Select over loading full graphs when only a few fields are needed.

  • Set explicit delete behaviors to avoid accidental cascades.

  • Use parameterized raw SQL with FromSql and interpolated parameters if you must drop to SQL.

21

Explain the concept of cascade deletion in Entity Framework and describe the available configuration methods.

Cascade delete automatically removes dependent entities from the database when their principal entity is deleted, preventing orphaned records. In EF Core, this behavior is defined by delete behaviors that can be customized using conventions or the Fluent API within the DbContext configuration.

Read full answer

Applies toEF Core 8 on .NET 8

How Cascade Delete Works

When a principal entity (e.g., a Blog) is deleted, the database action propagates to dependent entities (e.g., Posts) based on the configured behavior. This propagation can occur at the database level via foreign key constraints or at the application level via the EF Core change tracker before saving changes. Understanding this distinction is critical for performance and data integrity.

Configuring Delete Behavior

EF Core provides two ways to configure delete behavior:

  • Fluent API: The most explicit and flexible method. Inside OnModelCreating, you specify the relationship and call OnDelete with a DeleteBehavior enum value.

  • Conventions: EF Core automatically chooses a delete behavior based on foreign key nullability (see Default Conventions). There is no data annotation attribute for delete behavior in EF Core.

The available DeleteBehavior values are:

Behavior Description
Cascade Database-level cascade delete; the database removes dependent rows.
ClientCascade EF Core loads dependents into memory and deletes them individually; no database cascade.
Restrict Prevents deletion of the principal if dependents exist; throws an exception.
ClientSetNull EF Core sets the foreign key to null on the client side and updates the database.
SetNull Database-level set null; the foreign key column is set to NULL by the database.
SetDefault Database-level set default; the foreign key column is set to its default value.

Default Conventions

EF Core applies conventions based on foreign key nullability. If the foreign key property is non-nullable (required relationship), the default behavior is Cascade. If the foreign key is nullable (optional relationship), the default is ClientSetNull, which sets the FK to null instead of deleting the dependent. Developers should verify these defaults match business logic.

Multiple Cascade Paths

Database providers like SQL Server restrict multiple cascade paths leading to the same table to prevent cycles or ambiguous deletion orders. If two relationships cascade to the same table, EF Core throws an exception during migration creation; resolve this by setting one path to Restrict or ClientSetNull. This limitation requires careful schema design when entities share multiple relationships.

Code Example

using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

public class Blog
{
    public int Id { get; set; }
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Non-nullable FK => default is Cascade
    public Blog Blog { get; set; }
}

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Configure the relationship and explicitly set cascade delete
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .OnDelete(DeleteBehavior.Cascade);
    }
}

Best Practices

Always register your DbContext as scoped using AddDbContext in dependency injection to avoid concurrency issues and threading errors. Intentionally configure delete behaviors rather than relying on defaults to prevent accidental data loss, and test migration scripts thoroughly before applying them to production databases. Avoid deep cascade chains that might lock large portions of the database during transaction commits.

22

What is the approach for modeling a self-referencing relationship where an entity type links to itself in Entity Framework Core?

A self-referencing association in Entity Framework Core is modeled by defining a navigation property and foreign key within the same entity class, allowing instances to link to other instances of the same type. This is commonly used for hierarchical data such as organizational charts or category trees.

Read full answer

Applies toEF Core 8 on .NET 8

Entity Model Definition

To represent a hierarchy, the entity class contains a nullable foreign key property pointing to its own primary key. It also defines a reference navigation property for the parent and a collection navigation property for the children. The nullable foreign key is essential because the root node in the hierarchy has no parent. For example, a Category entity uses ParentId to store the identifier of its parent category, while Children holds all subcategories directly beneath it.

Fluent API Configuration

While EF Core conventions often detect self-referencing relationships automatically, explicit configuration using the Fluent API prevents ambiguity and controls cascade behavior. You configure the relationship by calling HasOne for the parent navigation and WithMany for the children collection. Crucially, you should set OnDelete(DeleteBehavior.NoAction) to avoid cascade delete cycles, which would throw an exception during migration creation because deleting a parent could recursively require deleting itself.

Querying Hierarchical Data

When reading data, use Include to eagerly load children and avoid the N+1 query problem. Since hierarchical reads are often read-only operations, append AsNoTracking() to improve performance by disabling change tracking. Be cautious when serializing results to JSON; the bidirectional navigation (Parent referencing Children referencing Parent) can cause reference loops. Configure JSON serialization to ignore cycles or use projections to flatten the data if only specific fields are needed.

Code Example

The following example demonstrates the entity definition, DbContext configuration, dependency injection registration, and a read-only query. Note that AddDbContext registers the context as scoped by default, ensuring each request gets a unique instance.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int? ParentId { get; set; }

    public Category? Parent { get; set; }
    public ICollection<Category> Children { get; set; } = new List<Category>();
}

public class AppContext : DbContext
{
    public DbSet<Category> Categories { get; set; }

    public AppContext(DbContextOptions<AppContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>()
            .HasOne(c => c.Parent)
            .WithMany(c => c.Children)
            .HasForeignKey(c => c.ParentId)
            .OnDelete(DeleteBehavior.NoAction);
    }
}

public class CategoryService
{
    private readonly AppContext _context;

    public CategoryService(AppContext context)
    {
        _context = context;
    }

    public async Task<List<Category>> GetCategoriesAsync()
    {
        return await _context.Categories
            .AsNoTracking()
            .Include(c => c.Children)
            .ToListAsync();
    }
}

Best Practices and Pitfalls

Always register the DbContext with a scoped lifetime using AddDbContext; never share a single context instance across threads or store it as a singleton. When updating hierarchy structures, prefer ExecuteUpdate (EF Core 7+) for bulk property changes rather than loading entities into memory. Avoid deep recursive loading with Include, as it can generate complex SQL; instead, load levels explicitly or use recursive CTEs via FromSqlInterpolated if the database supports it. Finally, ensure ParentId is nullable to accommodate root nodes, and validate that no circular references are created in application logic before saving changes.

23

How can you construct and execute data retrieval operations using LINQ within Entity Framework Core?

Entity Framework Core translates LINQ expressions written against DbSet<T> properties into SQL commands executed against the database. Developers compose queries using standard C# LINQ operators, which EF Core converts during runtime to fetch entities efficiently without writing raw SQL.

Read full answer

Applies toEF Core 8 on .NET 8

Core Query Mechanism

Queries are initiated through a DbContext instance, typically injected via dependency injection. Each entity type is exposed as a DbSet<T> property, serving as the root for LINQ operations. Standard operators like Where, Select, OrderBy, and FirstOrDefault are supported. The query returns an IQueryable<T>, meaning execution is deferred until a terminal method such as ToListAsync(), ToArray(), or Count() is called. This allows EF Core to compose the full SQL statement before sending it to the server.

Optimization Strategies

For read-only scenarios, disable change tracking to reduce memory overhead and improve performance. Use AsNoTracking() to indicate that entities returned should not be monitored for changes. When fetching related data, eager loading prevents multiple round-trips. Use Include and ThenInclude to load navigation properties in a single query, or use projections with Select to retrieve only necessary columns. Projections reduce network traffic by selecting specific fields rather than entire entity rows.

Bulk Operations

When modifying multiple records without loading them into memory, use bulk methods introduced in EF Core 7. ExecuteUpdate and ExecuteDelete translate directly to SQL UPDATE or DELETE statements. This avoids the overhead of materializing entities, making it significantly faster for large datasets compared to retrieving, modifying, and saving entities individually. These methods return the number of affected rows, and async variants such as ExecuteUpdateAsync are available for web applications.

Code Example

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public bool IsActive { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; } = null!;
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
}

public class ProductService
{
    private readonly AppDbContext _context;

    public ProductService(AppDbContext context)
    {
        _context = context;
    }

    public async Task<List<Product>> GetActiveProductsAsync()
    {
        // Read-only query with eager loading
        return await _context.Products
            .Where(p => p.IsActive)
            .Include(p => p.Category)
            .AsNoTracking()
            .ToListAsync();
    }

    public async Task<int> DiscountInactiveProductsAsync(decimal discount)
    {
        // Bulk update without loading entities
        return await _context.Products
            .Where(p => !p.IsActive)
            .ExecuteUpdateAsync(s => s.SetProperty(p => p.Price, p => p.Price * discount));
    }
}

In production, register the context as scoped in Program.cs: builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));

Best Practices and Pitfalls

  • Deferred Execution: Remember that LINQ queries run only when enumerated. Calling ToList() multiple times executes the SQL multiple times.

  • N+1 Queries: Avoid accessing navigation properties inside loops after fetching a list without Include.

  • Parameterization: LINQ queries are automatically parameterized, protecting against SQL injection. Never concatenate user input into raw SQL.

  • Scoped Lifetime: Always register DbContext as Scoped to ensure one instance per request and avoid concurrency issues.

  • Async Usage: Always use async methods like ToListAsync() in web applications to free up threads during I/O operations.

24

How do eager loading, lazy loading, and explicit loading differ in Entity Framework?

Eager loading, lazy loading, and explicit loading are three strategies Entity Framework uses to populate related data, differing in when the related query runs and who triggers it.

Read full answer

Applies toEF Core 8 on .NET 8

Eager Loading

Eager loading fetches related data as part of the original query, typically in a single round trip.

  • Uses Include and ThenInclude to join related entities.

  • Best when you know you will need the related data immediately.

  • Can produce large result sets if overused; prefer projections (Select) when only a few fields are needed.

Lazy Loading

Lazy loading defers the related query until the navigation property is actually accessed.

  • Requires the Microsoft.EntityFrameworkCore.Proxies package and virtual navigation properties.

  • Enabled via UseLazyLoadingProxies() in OnConfiguring or DI.

  • Convenient but prone to the N+1 query problem and hidden database calls inside loops or serialization.

  • Does not work with AsNoTracking queries by default because the proxy needs a tracked entity.

Explicit Loading

Explicit loading gives you manual control: you decide exactly when to load a navigation property.

  • Uses context.Entry(entity).Collection(...).Load() or .Reference(...).Load().

  • Useful when you only sometimes need related data, or when you want to load it conditionally after the main query.

  • Keeps the initial query small while still allowing on-demand loading.

Comparison

Strategy When it runs Who triggers it Round trips N+1 risk
Eager With original query Developer via Include Usually one Low
Lazy On property access Runtime proxy Many (hidden) High
Explicit On explicit Load() call Developer On demand Medium

Best Practices and Common Pitfalls

  • Prefer eager loading or projections for read-only screens to avoid N+1.

  • Use AsNoTracking() for read-only queries; note lazy loading proxies won’t fire on untracked entities.

  • Avoid lazy loading in serialization paths (e.g., returning entities from an API) because it can trigger queries during JSON serialization.

  • Use explicit loading when the need for related data depends on runtime conditions.

  • For bulk updates/deletes, use ExecuteUpdate/ExecuteDelete (EF Core 7+) instead of loading entities.

25

What mechanism does Entity Framework use to convert LINQ expressions into executable SQL statements?

Entity Framework Core converts LINQ queries into SQL through a query pipeline that turns C# expression trees into database commands. The query is not executed when it is composed; execution and SQL generation happen when results are enumerated, such as by calling ToList().

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

  • Expression tree: A LINQ query against DbSet<T> is represented as an IQueryable<T> expression tree rather than compiled delegates.

  • Query provider: EF Core’s LINQ provider receives the tree and translates it into an internal query model.

  • SQL generation: The provider maps operations such as filtering, projection, joins, and ordering to SQL for the configured database provider.

  • Parameterization: Values captured from the C# expression become SQL parameters, which helps prevent SQL injection and allows plan reuse.

Translation Steps

  1. Parse the expression tree: Identify operations such as Where, Select, OrderBy, and Join.

  2. Build and optimize the query model: Combine predicates and remove redundant operations where possible.

  3. Generate provider-specific SQL: Render the final command using the dialect of the configured provider, such as SQL Server or PostgreSQL.

  4. Execute and materialize: Send the command to the database, then map returned rows to .NET objects.

Client vs Server Evaluation

EF Core 8 does not silently evaluate untranslatable parts of a query on the client. If a method, property, or expression cannot be translated to SQL, enumeration throws an InvalidOperationException. Use AsEnumerable() only after retrieving the needed data when client-side processing is intentional.

Best Practices

  • Use AsNoTracking: Apply AsNoTracking() for read-only queries to avoid change-tracking overhead.

  • Keep predicates translatable: Prefer simple comparisons and supported methods; move complex calculations to database functions or computed columns.

  • Inspect generated SQL: Enable logging during development to verify translation and performance.

  • Test translation early: Exercise queries in tests so unsupported expressions fail before production.

26

Does Entity Framework allow LINQ queries to shape results into classes that are not mapped as entities?

Yes, Entity Framework Core allows you to shape query results into classes that are not mapped as entities using LINQ projection. This lets you return lightweight Data Transfer Objects (DTOs) or view models directly from the database without instantiating full entity objects.

Read full answer

Applies toEF Core 8 on .NET 8

How Projection Works

When you call Select in a LINQ query, EF Core analyzes the expression tree and translates it into SQL. The generated SQL selects only the columns required to populate the target type. Because the result type is not an entity, the context does not track changes on these objects, reducing memory overhead. This translation happens server-side, so the database performs the shaping before sending data over the network.

Supported Target Types

You can project into several formats depending on your architectural needs: - Anonymous types: Useful for internal queries where the type does not need to leave the method scope. - Concrete DTOs: Plain C# classes with public properties and a parameterless constructor. - Tuples: Supported in modern C# versions for lightweight grouping without defining new classes. The target class requires no EF Core attributes, such as [Key] or [Table], and does not need to be registered in the DbContext model configuration.

Performance Benefits

Projection minimizes network traffic by fetching only necessary columns rather than entire rows. It also avoids the cost of change tracking proxies and identity resolution. For read-only operations, combine projection with AsNoTracking() to ensure the query is optimized for retrieval. This is particularly beneficial when dealing with wide tables or large datasets where only a subset of data is needed for display. Reducing the data load can also improve query execution time on the database server.

Common Pitfalls and Best Practices

  • Translation failures: If EF Core cannot translate part of the projection, it may throw rather than silently pulling all rows into memory. Check generated SQL and logs during development.

  • Constructor limitations: Prefer object initializers with public settable properties; constructor-based projections may not translate for every database provider.

  • DbContext lifetime: Register your DbContext as Scoped via AddDbContext in dependency injection. Never share a single context instance across multiple threads or store it as a Singleton.

  • Nullability: Ensure property types in the DTO match the nullability of the database columns to avoid runtime exceptions when materializing results.

  • Navigation properties: Projecting navigation properties can require joins and may fail translation if the path is not supported; keep projections flat when possible.

27

What does the term LINQ to Entities mean in Entity Framework?

LINQ to Entities is the LINQ provider that translates .NET LINQ expressions into store-specific SQL so queries execute against the database instead of in memory. In EF6 (.NET Framework) this provider is explicitly called LINQ to Entities; in EF Core 8 the same capability is provided by EF Core’s built-in LINQ provider.

Read full answer

Applies toEF6 (.NET Framework) for the classic "LINQ to Entities" name; EF Core 8 for the equivalent built-in LINQ provider.

How It Works

  • You write a query against a DbSet or IQueryable using standard LINQ operators such as Where, Select, OrderBy, and GroupBy.

  • The provider builds an expression tree from those operators.

  • EF translates that expression tree into SQL tailored to the configured database provider.

  • The database executes the SQL and returns results, which EF materializes into entity instances or projected types.

EF6 vs EF Core 8

Aspect EF6 (.NET Framework) EF Core 8 (.NET 8)
Provider name LINQ to Entities EF Core LINQ provider
Query translation Translates LINQ to SQL for the configured provider Translates LINQ to SQL for the configured provider
Client evaluation Throws NotSupportedException for non-translatable expressions Throws by default for non-translatable expressions; stricter than EF Core 1/2
Tracking Entities tracked by default Entities tracked by default; use AsNoTracking for read-only queries

Key Points

  • Queries are deferred: no database call occurs until you enumerate the results with ToList, FirstOrDefault, Count, or similar.

  • Only translatable expressions become SQL; unsupported logic may need to be moved after materialization or rewritten.

  • IQueryable keeps the query composable, while IEnumerable forces in-memory evaluation.

  • Projections with Select reduce data transfer and help avoid N+1 query problems.

Best Practices

  • Use AsNoTracking for read-only queries to reduce overhead.

  • Prefer Include/ThenInclude or projections over lazy loading to avoid N+1 queries.

  • Use ExecuteUpdate and ExecuteDelete for bulk changes instead of loading entities first.

  • Keep filter and sort logic translatable, and validate any user-supplied values before they reach the query.

28

What is the process for configuring and executing code-first migrations in Entity Framework Core?

Code-first migrations in EF Core let you incrementally update a database schema to match changes in your C# entity models by configuring a DbContext, generating migration snapshots with the EF CLI, and applying them to the target database.

Read full answer

Applies toEF Core 8 on .NET 8

DbContext Configuration and Registration

Define a class deriving from DbContext with DbSet<T> properties and configure relationships in OnModelCreating. Register the context in Program.cs with AddDbContext, which gives it a Scoped lifetime by default. A scoped instance is created per request or per logical operation, avoiding cross-thread sharing and tracking conflicts. Keep connection strings in configuration rather than hardcoding them, and ensure Microsoft.EntityFrameworkCore.Design is referenced so the CLI can instantiate your context at design time.

Generating Migration Scripts

Install the EF CLI tool with dotnet tool install --global dotnet-ef (or as a local tool). After changing entities, run dotnet ef migrations add <MigrationName> from the project directory. EF Core compares the current model with the previous <ContextName>ModelSnapshot.cs and generates a migration class containing Up and Down methods. Always review the generated diff before committing, because migrations can drop columns or tables and cause data loss.

Applying Migrations to Database

Run dotnet ef database update to apply all pending migrations. You can target a specific migration by name, or use 0 to revert the database to an empty state. In production or containerized apps, it is common to call context.Database.Migrate() inside a service scope at startup so the schema is current before requests are handled. Avoid calling Migrate from a singleton or from multiple threads simultaneously.

Code Example

The following snippets show a minimal entity, a DbContext that receives options through DI, and the scoped registration in Program.cs.

using Microsoft.EntityFrameworkCore;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;

// In Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

Best Practices and Pitfalls

  • Inspect migration diffs: Generated migrations may contain destructive operations; edit them manually when safe or split them into separate migrations.

  • Use a design-time factory: If your context requires complex construction, implement IDesignTimeDbContextFactory<T> so dotnet ef commands work without running the full app.

  • Avoid long-lived contexts: Never store a DbContext as a singleton or share it across threads.

  • Protect data: Back up production databases before applying migrations, and test migrations against a copy of production data.

  • Parameterize raw SQL: If you execute custom SQL during migrations or queries, use FromSqlInterpolated or ExecuteSqlInterpolated; never concatenate user input into FromSqlRaw.

29

Is it possible to revert a database to an earlier schema state using Entity Framework Core migrations?

Yes, Entity Framework Core allows you to revert a database to an earlier schema state by applying the reversal logic defined within previous migrations. This operation is typically called rolling back a migration and is executed by targeting a specific migration name rather than the latest one. Each migration contains both an Up method to apply changes and a Down method to undo them, enabling bidirectional schema evolution during development and deployment.

Read full answer

Applies toEF Core 8 on .NET 8

How Migration Reversal Works

When you add a migration using Add-Migration or dotnet ef migrations add, EF Core scaffolds two methods inside the generated class. The Up method contains the operations to move the schema forward, such as creating tables or adding columns. The Down method contains the inverse operations, such as dropping tables or removing columns. When you request a database update targeting an earlier migration, EF Core identifies the current state by querying the __EFMigrationsHistory table and executes the Down methods of all migrations newer than the target in reverse order.

Commands to Roll Back

You can perform a rollback using either the Package Manager Console in Visual Studio or the .NET CLI. Both tools require the name of the target migration you wish to revert to. For example, use Update-Database -Migration <TargetMigration> in the Package Manager Console or dotnet ef database update <TargetMigration> from the CLI. If you specify a migration that is older than the current applied migration, EF Core executes the necessary Down scripts. To revert all changes and return the database to an empty state, target the zero migration with 0.

Risks of Data Loss

Rolling back migrations can be destructive depending on the operations defined in the Down methods. If a previous migration added a column with data, reverting it usually drops that column, resulting in permanent data loss. Similarly, dropping a table deletes all records within it. EF Core does not automatically back up data before executing these commands. You must review the generated Down code to understand the impact before running it against any environment containing valuable data.

Best Practices and Common Pitfalls

  • Backup First: Always take a database snapshot or backup before running rollback commands on non-development environments.

  • Avoid Production Rollbacks: In production pipelines, prefer forward-only migrations. Rolling back in production is risky and often indicates a flawed deployment strategy.

  • Review Generated Code: Inspect the Down method in the migration file to ensure it matches expectations, especially if custom SQL was involved.

  • Team Synchronization: If multiple developers share a database, reverting a migration others have applied causes synchronization issues. Treat migration history as shared state.

  • Transactions: Where possible, ensure migrations run within transactions so that partial failures do not leave the schema in an inconsistent state.

30

What mechanisms does Entity Framework Core provide for initializing a database with default data?

The standard approach for defining initial data in Entity Framework Core is configuring seed records directly in the model using HasData, which embeds insert statements into generated migrations. This ensures static reference data is deployed alongside schema changes without requiring separate scripts.

Read full answer

Applies toEF Core 8 on .NET 8

Built-in Seeding with HasData

The HasData method is called within the OnModelCreating override of your DbContext. It accepts entity instances that EF Core translates into INSERT statements during migration generation. This is ideal for static lookup tables such as roles, statuses, or categories that rarely change. Because the data lives in the migration history, applying migrations to a new database automatically populates these rows.

Limitations of HasData

This method only supports static values known at compile time. You cannot use dynamic expressions like DateTime.Now or environment variables inside HasData. Additionally, updating seeded data typically requires creating a new migration rather than modifying existing ones, to preserve migration history integrity. Primary keys usually must be explicitly specified to avoid conflicts across different environments.

Custom Seeding Logic

For dynamic data or complex initialization, execute logic after migrations are applied. In a minimal API, call context.Database.Migrate() then check for existing records before inserting. When verifying existence, use AsNoTracking to avoid attaching entities to the change tracker unnecessarily. This approach allows runtime values and conditional logic but requires idempotent code to prevent duplicates on restart. Typically, this logic resides in an extension method invoked during startup, ensuring the database is ready before the application begins serving requests.

Best Practices and Pitfalls

  • DbContext Lifetime: Register the context as Scoped using AddDbContext in DI; never instantiate it as a Singleton or share it across threads.

  • Idempotency: Custom seeding scripts must check if data exists before inserting to prevent primary key violations on application restart.

  • Security: Never hardcode secrets or credentials in seed data; use user secrets or environment variables injected into the startup logic.

  • Performance: For large datasets, avoid HasData as it bloats migration files; use bulk insert tools or SQL scripts instead.

  • Tracking: When checking for existing records in custom seeds, always use AsNoTracking to reduce memory overhead and change tracking noise.

  • Migrations: Commit generated migrations to source control so team members and deployment pipelines apply the same schema and seed state.

31

In what situation would you run the Update-Database command?

Update-Database is the Package Manager Console command that applies pending EF Core migrations to a target database, creating the database first if it does not already exist.

Read full answer

Applies toEF Core 8 on .NET 8

What It Does

  • Migration comparison: reads the migration files in the startup project and compares them against the __EFMigrationsHistory table in the target database.

  • Selective application: applies only migrations that are not already recorded as applied.

  • Database creation: creates the database and schema from scratch when the target database does not exist (provider-dependent).

  • Targeted migration: can move the database forward to a named migration or roll back to an earlier migration when a migration name is supplied.

Typical Scenario

During local development after a model change. For example, after adding an Email property to a Customer entity, you run Add-Migration AddCustomerEmail to generate a migration, then run Update-Database to apply that change to the development database before testing the application.

Applying or Rolling Back

  • Apply all pending migrations: run Update-Database with no arguments.

  • Target a specific migration: pass the migration name to apply up to it or roll back to it.

  • Multiple contexts: use the -Context parameter only when the project contains more than one DbContext and you need to target a specific one.

Best Practices and Pitfalls

  • Use for development and controlled deployment: do not run it automatically from every application instance at startup.

  • Review migrations first: inspect generated migrations before applying them, especially for destructive operations such as dropping columns or renaming tables.

  • Match connection settings: use the same connection string and environment settings intended for the target database, and never embed secrets in scripts or source control.

  • Prefer scripted deployment in production: use generated SQL scripts, migration bundles, or a reviewed CI/CD step so schema changes are applied once and audited.

32

In a collaborative development setting, what steps do you take to manage and resolve Entity Framework Core migration conflicts?

Resolving migration conflicts in Entity Framework Core means reconciling divergent migration files and the model snapshot so the team keeps one linear migration history, then validating that every migration applies cleanly to a fresh database.

Read full answer

Applies toEF Core 8 on .NET 8

How Conflicts Occur

Conflicts arise when two developers modify the DbContext model independently and both run dotnet ef migrations add before sharing their work. Each migration contains an Up method, a Down method, and a reference to the model snapshot. Git flags conflicts in the migration C# files and potentially in YourDbContextModelSnapshot.cs because both files reflect divergent states of the same model history.

Step-by-Step Resolution

To resolve these conflicts safely, follow this sequence: 1. Pull Latest Changes: Fetch the remote branch containing the other developer’s migration. 2. Remove or Reconcile Your Local Migration: If your migration has not been applied or shared, remove it with dotnet ef migrations remove; otherwise resolve the conflicting migration and snapshot files manually. 3. Regenerate from the Merged Model: Run dotnet ef migrations add MergeConflictFix so EF Core scaffolds one migration from the final merged model state. 4. Inspect Generated Code: Compare the new migration against the conflicting attempts to ensure no operations are duplicated or lost. 5. Update Snapshot: Ensure YourDbContextModelSnapshot.cs matches the final model state after merging. 6. Test Locally: Apply all migrations to a fresh database and verify the sequence works end to end.

Team Best Practices

Preventing conflicts is more efficient than resolving them. Adopt these habits: - Small Migrations: Commit model changes frequently to reduce the window for overlapping work. - Communication: Announce when you are generating a migration that affects shared tables. - CI/CD Validation: Configure pipelines to build the project and verify migrations apply cleanly on a fresh database. - Single Source of Truth: Treat the DbContext model as the authority, not the database schema.

Merged Migration Example

A resolved migration typically combines operations from conflicting attempts into a single linear history. Below is a simplified example of a merged migration class structure.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;

public partial class MergeConflictFix : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        // Operation from Developer A
        migrationBuilder.AddColumn<string>(
            name: "Email",
            table: "Users",
            nullable: true);

        // Operation from Developer B
        migrationBuilder.CreateIndex(
            name: "IX_Users_Email",
            table: "Users",
            column: "Email");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropIndex(
            name: "IX_Users_Email",
            table: "Users");

        migrationBuilder.DropColumn(
            name: "Email",
            table: "Users");
    }
}

Common Pitfalls

Avoid these mistakes during conflict resolution: - Deleting Applied Migrations: Never delete migrations that have been applied to production databases; create a new corrective migration instead. - Ignoring Snapshot Errors: If the snapshot does not match the model, EF Core will throw errors during subsequent migration generation. - Manual SQL Editing: Avoid hand-writing SQL in migrationBuilder.Sql unless necessary, as it bypasses model validation. - DbContext Lifetime: Register the DbContext with AddDbContext as Scoped, and never share one across threads or keep it as a Singleton.

33

Explain the N+1 query problem and describe the strategies to prevent it when using Entity Framework Core.

The N+1 problem occurs when an application runs one query to load N parent entities and then issues an additional query for each parent to load related data, causing N+1 database round trips. In EF Core, you avoid it by eagerly loading related data, projecting only required columns, or batching related queries.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

  • A first query loads the parent rows, such as all blogs.

  • Code then iterates over those rows and accesses a navigation property, such as blog.Posts.

  • If lazy loading is enabled, or the code explicitly queries per parent, EF Core emits one extra SQL statement per parent.

  • The result is N+1 queries instead of one, which scales poorly and multiplies network and database overhead.

Strategies to Avoid It

  • Eager loading with Include/ThenInclude: loads related data in the same query, so one round trip covers parents and children.

  • Projection with Select: builds a result containing only the needed columns, reducing payload size and avoiding loading full entities.

  • Split queries with AsSplitQuery: issues a small number of separate queries instead of one large cartesian product, useful when including multiple collections.

  • Explicit loading with Load: fetches related data on demand for a single entity when you genuinely need it only sometimes, but avoid calling it inside loops.

  • Avoid lazy loading in read paths and use AsNoTracking for read-only queries to prevent accidental per-entity queries.

Common Pitfalls

  • Including several collections at once can cause a cartesian explosion; prefer AsSplitQuery in that case.

  • Accessing navigation properties in a loop after materializing parents is the most common trigger of N+1.

  • Lazy loading hides the problem during development but surfaces it under load in production.

Best Practices

  • Default to Include for the data you know you need, and Select projections for read-only screens.

  • Use AsSplitQuery when including two or more collections to avoid row multiplication.

  • Keep AsNoTracking on read-only queries to reduce overhead and prevent accidental lazy loads.

  • Profile generated SQL with logging or a tool such as MiniProfiler to catch N+1 before production.

34

What effect does the change tracking mechanism have on Entity Framework Core performance, and when is it appropriate to disable it?

Change tracking maintains entity state snapshots, enabling automatic updates but incurring memory and CPU overhead. Disabling it via AsNoTracking significantly boosts read performance when modifications aren’t needed.

Read full answer

Applies toEF Core 8 on .NET 8

How Change Tracking Works

EF Core 8 uses snapshot-based change tracking by default. When an entity is queried, the context stores a copy of its original values in the ChangeTracker. During SaveChanges, it compares current values against these snapshots to determine which properties changed and generates the corresponding SQL UPDATE statements. This automation simplifies development but requires resources to maintain state.

Performance Implications

Tracked entities consume more memory because both current and original values are stored. Query materialization is slower since each entity must be registered with the context. Additionally, SaveChanges performance degrades as the number of tracked entities increases because every entity must be inspected for changes. For read-heavy workloads, this overhead provides no benefit if no updates occur. Large result sets with tracking enabled can lead to significant memory pressure and slower response times.

When to Disable Tracking

Use AsNoTracking for read-only operations such as reporting, dashboards, or lookup data retrieval. It prevents the context from storing snapshots, reducing memory pressure and speeding up materialization. If you need to update entities later, you must either re-attach them or load them with tracking enabled. For bulk updates where entities aren’t needed in memory, prefer ExecuteUpdate over loading and modifying entities. This avoids the cost of materialization entirely.

Best Practices

Register your DbContext as Scoped using AddDbContext to ensure change tracking state does not leak across requests. Avoid tracking large result sets; project only needed fields using Select if full entities aren’t required. Never share a DbContext instance across threads, as change tracking is not thread-safe. Use AsNoTrackingWithIdentityResolution if you need object identity without modification tracking. Always prefer ExecuteUpdate or ExecuteDelete for bulk operations to bypass tracking overhead completely.

35

How can you improve the performance of Entity Framework Core queries and data access?

Entity Framework Core performance is usually improved by reducing tracked entities, avoiding unnecessary round trips, and pushing bulk work to the database.

Read full answer

Applies toEF Core 8 on .NET 8

Query Efficiency

  • AsNoTracking: Use AsNoTracking() for read-only queries so EF Core skips change tracking and snapshot creation.

  • Projection: Project only the columns you need with Select, instead of loading full entities.

  • Include/ThenInclude: Use Include and ThenInclude deliberately for related data, or prefer projections when you only need a subset of related fields.

  • AsSplitQuery: Use AsSplitQuery() for large eager-loading graphs to avoid cartesian explosion from a single SQL join.

  • Pagination: Paginate with Skip and Take, and always apply filtering before ordering and paging.

  • Compiled queries: Use EF.CompileQuery or EF.CompileAsyncQuery for hot paths that run repeatedly with the same shape.

Bulk Operations

  • ExecuteUpdate/ExecuteDelete: Use ExecuteUpdate and ExecuteDelete for bulk changes so the work happens in SQL instead of loading entities into memory.

  • Batching: Avoid calling SaveChanges once per entity; batch related changes and save once.

  • Change detection: Disable automatic change detection where appropriate by using AsNoTracking() or explicitly managing tracking for read-only workflows.

Context and Connection Usage

  • Scoped context: Register DbContext as scoped with AddDbContext; never share one instance across threads or keep it as a singleton.

  • Context pooling: Consider AddDbContextPool for high-throughput applications to reuse context instances.

  • Connection lifetime: Open connections only when needed, use async methods with CancellationToken, and dispose contexts promptly.

  • Indexes: Ensure database indexes support your Where, OrderBy, and join predicates.

Raw SQL and N+1 Avoidance

  • Parameterized raw SQL: Use FromSqlInterpolated or ExecuteSqlInterpolated with interpolated parameters; never concatenate user input into FromSqlRaw.

  • N+1 detection: Watch for N+1 queries by inspecting generated SQL and using eager loading or projections.

  • Identity resolution: Use AsNoTrackingWithIdentityResolution() only when you need identity resolution without full change tracking.

Code Example

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

// Minimal entity types.
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public bool IsActive { get; set; }
    public int CategoryId { get; set; }
}

public class Order { public Customer Customer { get; set; } }
public class Customer { public Address Address { get; set; } }
public class Address { public int Id { get; set; } }

public record ProductSummary(int Id, string Name, decimal Price);

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Order> Orders { get; set; }
}

public class ProductService
{
    private readonly AppDbContext _context;

    public ProductService(AppDbContext context) => _context = context;

    public async Task<List<ProductSummary>> RunAsync(int categoryId, CancellationToken cancellationToken)
    {
        // Read-only projection with no tracking.
        var products = await _context.Products
            .AsNoTracking()
            .Where(p => p.IsActive)
            .Select(p => new ProductSummary(p.Id, p.Name, p.Price))
            .ToListAsync(cancellationToken);

        // Eager loading with split query to avoid cartesian explosion.
        var orders = await _context.Orders
            .AsSplitQuery()
            .Include(o => o.Customer)
            .ThenInclude(c => c.Address)
            .ToListAsync(cancellationToken);

        // Bulk update executed directly in SQL (parameterized).
        await _context.Products
            .Where(p => p.CategoryId == categoryId)
            .ExecuteUpdateAsync(setters => setters
                .SetProperty(p => p.Price, p => p.Price * 1.10m),
                cancellationToken);

        return products;
    }
}

Best Practices and Common Pitfalls

  • Async APIs: Prefer async APIs such as ToListAsync, FirstOrDefaultAsync, and SaveChangesAsync.

  • Materialization: Avoid calling ToList() too early, which can force client-side evaluation or unnecessary materialization.

  • Tracking: Do not track entities you only display or serialize.

  • Measurement: Measure with logging, EnableSensitiveDataLogging only in development, and database profiling tools before optimizing.

  • Singleton DbContext: Do not use a singleton DbContext to “improve performance”; it causes concurrency and stale-data bugs.

  • Load-to-update: Do not load entities just to update one column; use ExecuteUpdate.

  • Global tracking: Do not disable tracking globally if some workflows require change tracking; scope AsNoTracking() to read-only queries.

36

What practices help optimize database connection handling and lifecycle management in Entity Framework?

Optimizing EF Core connection handling centers on keeping DbContext instances short-lived and scoped, letting ADO.NET pooling reuse physical connections, and avoiding patterns that hold connections or contexts open.

Read full answer

Applies toEF Core 8 on .NET 8

How the Lifecycle Works

  • EF Core does not open a connection when you create a DbContext; it opens one lazily on the first database round-trip.

  • After the operation finishes, or when the context is disposed, the connection is closed and returned to the ADO.NET pool rather than destroyed.

  • A single DbContext instance is not thread-safe and must not be shared across concurrent requests or threads.

Recommended Practices

  • Register the context with AddDbContext as Scoped in ASP.NET Core so each request gets its own instance that is disposed at request end.

  • For high-throughput scenarios, use AddDbContextPool to rent and reuse DbContext instances, reducing allocation overhead.

  • Keep the context lifetime as short as possible and dispose it promptly through using or the DI scope.

  • Use AsNoTracking() for read-only queries to skip change tracking and reduce memory use.

  • Avoid N+1 queries with Include/ThenInclude or projections.

  • Use ExecuteUpdate/ExecuteDelete for bulk changes instead of loading entities.

  • Keep connection pooling enabled in the connection string, which is the default for SQL Server.

Common Pitfalls

  • Do not register DbContext as a Singleton or share it across threads.

  • Do not hold a context open across long-running work or multiple unrelated requests.

  • Do not disable connection pooling unless you have a specific reason.

  • Avoid keeping transactions open longer than necessary.

37

What mechanisms does Entity Framework Core provide for managing concurrent data access and updates?

Entity Framework Core primarily relies on optimistic concurrency control, detecting conflicts when changes are saved rather than locking database rows during reads. This approach assumes conflicts are rare and allows higher throughput by avoiding long-held database locks.

Read full answer

Applies toEF Core 8 on .NET 8

How Optimistic Concurrency Works

When an entity is updated, EF Core includes the original values of configured concurrency tokens in the WHERE clause of the generated UPDATE statement. If another transaction modified the row after it was loaded, the original values no longer match, zero rows are affected, and EF Core throws a DbUpdateConcurrencyException. This ensures data integrity without blocking readers.

Configuring Concurrency Tokens

No property is a concurrency token by default; configure one explicitly. A rowversion/timestamp column is the most reliable mechanism because the database changes it automatically on every update. - Fluent API: Use IsConcurrencyToken() for specific properties or IsRowVersion() for byte[] rowversion columns. - Data Annotations: Apply [ConcurrencyCheck] to properties you want validated, or [Timestamp] to a byte[] rowversion property. - Common choices: A rowversion column, a last-modified timestamp, or another property whose original value must still match at save time.

Handling Concurrency Exceptions

When a conflict occurs, catch DbUpdateConcurrencyException and inspect ex.Entries to choose a resolution strategy. Common approaches include reloading the entity from the database (store wins), overwriting database values (client wins), or merging changes manually. ReloadAsync() refreshes an entry with current database values; GetDatabaseValues() can be used to compare or merge values before retrying.

Pessimistic Concurrency via Raw SQL

EF Core does not natively support pessimistic locking in LINQ queries. On SQL Server, you can use raw SQL with locking hints such as UPDLOCK, always with interpolated parameters to avoid SQL injection. Locking syntax and behavior are database-specific, so use the hints supported by your provider and keep transactions short.

Best Practices and Pitfalls

  • Scoped DbContext: Register the context with AddDbContext so each unit of work is short-lived; never share a DbContext across threads or register it as a Singleton.

  • Avoid Long Transactions: Keep transactions short to reduce lock contention and timeout risks.

  • Retry Logic: Implement exponential backoff retries for transient concurrency conflicts rather than failing immediately.

  • Read-Only Queries: Use AsNoTracking() for queries that do not update data to avoid unnecessary change tracking overhead.

  • Bulk Updates: For bulk operations where individual concurrency checks are not required, use ExecuteUpdate (EF Core 7+) to bypass change tracking.

38

How does Entity Framework Core manage simultaneous data modifications through optimistic concurrency control?

Optimistic concurrency control in Entity Framework Core lets multiple transactions read and modify data without locks, detecting conflicts at persistence time by comparing the original values tracked in the context with the current database values during SaveChanges.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

  • When an entity is queried, EF Core snapshots its original values. On SaveChanges, it generates an UPDATE (or DELETE) whose WHERE clause contains the primary key plus every original concurrency-token value.

  • If another transaction changed any token column after the entity was loaded, the WHERE clause matches no rows. The database reports zero affected rows and EF throws DbUpdateConcurrencyException.

  • Without a concurrency token, EF uses last-write-wins and silently overwrites concurrent changes.

  • Because no locks are held during the read-modify window, throughput and scalability stay high; the cost is a possible conflict that application code must resolve.

Configuring Concurrency Tokens

Approach Syntax Behavior
Row version [Timestamp] on a byte[] property, or Property(p => p.RowVersion).IsRowVersion() SQL Server automatically regenerates the value on every write; guarantees detection of any concurrent modification.
Explicit token [ConcurrencyCheck] or IsConcurrencyToken() on a business property such as Price Only changes to that property cause a conflict; concurrent edits to other columns go unnoticed.
  • A row version is the safest choice because it is database-generated and immutable from application code.

  • Marking business columns works when only that column matters, but it can miss conflicts and can reject legitimate updates if the column changes for unrelated reasons.

Handling Conflicts

  • Catch DbUpdateConcurrencyException and inspect ex.Entries() to identify the conflicting entities.

  • Reload the entity with entry.ReloadAsync() to see the current database state, then either merge changes, reapply the business operation on fresh data, or tell the user their copy is stale.

  • A naive retry loop can overwrite another user’s work; always reload or re-fetch before retrying.

  • For API scenarios, returning HTTP 409 with the current values lets the client decide.

Best Practices and Pitfalls

  • Scoped DbContext: Register the context with AddDbContext so each request gets its own instance. Never share one context across threads or store it as a Singleton; that corrupts change tracking and accumulates entities.

  • Read-only queries: Use AsNoTracking when data will not be saved. This avoids snapshotting original values and reduces memory pressure.

  • Bulk operations: ExecuteUpdate and ExecuteDelete (EF Core 7+) bypass change tracking and do not automatically check concurrency tokens. Add an explicit Where condition on a token if you need optimistic protection.

  • Token selection: Prefer IsRowVersion() / [Timestamp] over [ConcurrencyCheck] on volatile business data. Row versions detect every modification; business columns may change legitimately without indicating a real conflict.

  • Transaction scope: Keep the read-modify-write window short. Long-lived contexts increase conflict probability and memory usage.

39

Which concurrency control strategies can be implemented with Entity Framework Core?

Optimistic concurrency is the main strategy EF Core supports out of the box: it detects conflicts at SaveChanges by including a concurrency token in the WHERE clause and throwing DbUpdateConcurrencyException. You can also implement pessimistic concurrency manually through database locking primitives, and if no token is configured EF Core silently follows last-writer-wins.

Read full answer

Applies toEF Core 8 on .NET 8

Optimistic Concurrency with Concurrency Tokens

  • EF Core adds the original value of every concurrency token to the WHERE clause of generated UPDATE/DELETE statements.

  • If another process changed the row, zero rows are affected and EF Core throws DbUpdateConcurrencyException.

  • The most common token is a byte[] rowversion column mapped with [Timestamp] or IsRowVersion(); any property can be a token via IsConcurrencyToken(true).

  • Rowversion values are generated by the database, so the property should not be assigned in application code.

Conflict Resolution

After catching DbUpdateConcurrencyException, inspect ex.Entries and compare CurrentValues, OriginalValues, and GetDatabaseValuesAsync(): - Client wins: keep the application’s changes, reset OriginalValues to the database values, then retry SaveChangesAsync. - Database wins: reload the entity or copy database values into CurrentValues, discarding local changes. - Merge: compare property-by-property and build a resolved value set before retrying. - If GetDatabaseValuesAsync() returns null, the row was deleted and must be handled separately.

Pessimistic Concurrency

  • EF Core has no built-in pessimistic locking API; you must use raw SQL inside an explicit transaction.

  • On SQL Server this means SELECT ... WITH (UPDLOCK, HOLDLOCK); on PostgreSQL SELECT ... FOR UPDATE.

  • Keep the transaction scope as short as possible, because held locks block other users and reduce throughput.

  • Always pass parameters to raw SQL via interpolated FromSqlInterpolated/ExecuteSqlInterpolated rather than concatenating strings.

Last-Writer-Wins

  • Without a concurrency token, SaveChanges updates based only on key columns, so the last writer overwrites earlier changes without warning.

  • This is only safe for single-writer scenarios, append-only data, or fields where stale overwrites are acceptable.

Comparison

Strategy Built-in When to use Risk
Optimistic Yes Most web/API workloads Requires conflict handling
Pessimistic No, manual Short critical sections Blocking, scalability cost
Last-writer-wins Default behavior Single-writer or low-value data Silent data loss

Best Practices and Pitfalls

  • Register DbContext as scoped with AddDbContext; never share one instance across threads or store it as a singleton.

  • Use AsNoTracking for read-only queries to avoid unnecessary change tracking.

  • Prefer Include/ThenInclude or projections to prevent N+1 queries when loading related data needed for merge logic.

  • Do not treat DbUpdateConcurrencyException as a fatal error; log it and let the user decide how to resolve.

  • Avoid long-running transactions for pessimistic locking, especially under high concurrency.

40

What are T4 templates and how are they used with Entity Framework?

T4 (Text Template Transformation Toolkit) templates are design-time code-generation files (.tt) that produce C# (or other) source code when run in Visual Studio. In the Entity Framework world they were most prominent in EF6, where they generated entity classes and a DbContext from an EDMX model; EF Core does not use EDMX and instead relies on scaffolding, though T4 can still be used for custom generation.

Read full answer

Applies toEF6 (.NET Framework) for the classic EDMX workflow; EF Core 8 (.NET 8) only for optional custom code generation.

What T4 Templates Are

  • Files with a .tt extension containing a mix of literal text and control logic (C# or VB).

  • Executed at design time in Visual Studio to emit generated source files (.cs) that are compiled into the project.

  • Not a runtime feature: the generated code is what actually runs, and the template itself is not deployed.

T4 Templates in EF6

  • EF6 Database First and Model First workflows used an EDMX model as the source of truth.

  • Visual Studio shipped (and still ships) T4 templates such as DbContext.tt and EntityType.tt that read the EDMX and generate the DbContext and entity classes.

  • Developers could customize these templates to change naming, add attributes, or generate additional code (for example, partial classes or DTOs) without editing the generated files directly.

  • Regenerating from the EDMX overwrote the generated files, so customizations had to live in the .tt files or in separate partial classes.

T4 Templates in EF Core

  • EF Core has no EDMX and no built-in T4-based generation pipeline.

  • The recommended way to generate a model from an existing database is scaffolding: dotnet ef dbcontext scaffold (or the Package Manager Console equivalent), which produces a DbContext and entity classes directly.

  • T4 templates can still be added to an EF Core project for custom scenarios, such as generating repository classes, DTOs, or additional partial members from a hand-maintained model, but they are not part of the standard EF Core workflow.

  • For repeatable generation in EF Core, prefer scaffolding plus partial classes, or source generators, over maintaining T4 templates.

How They Work

  • A template is saved in the project; Visual Studio runs the transformation (manually or on save/build, depending on tooling) and writes the output file next to it.

  • The template can read metadata (an EDMX in EF6, or any file/database in custom templates) and emit code using control blocks.

  • Generated files are typically marked as dependent on the template so they are not edited by hand.

Best Practices and Pitfalls

  • Never edit generated files directly; put custom logic in partial classes or change the template.

  • Keep templates under source control and regenerate after model changes so generated code stays in sync.

  • T4 tooling is Windows/Visual Studio-centric; running transformations on Linux/macOS build agents requires additional tooling and is not always supported, so prefer scaffolding or source generators for cross-platform pipelines.

  • Do not treat T4 as a runtime templating engine; it is a design-time code generator.

  • When migrating from EF6 to EF Core, replace EDMX-plus-T4 workflows with dotnet ef dbcontext scaffold and partial classes rather than porting the templates verbatim.

41

Can you explain what Code-First Data Annotations are and describe how they configure entity models in Entity Framework?

Code-First Data Annotations are declarative attributes applied to entity classes and properties to configure database mapping directly within the C# model. They allow developers to define schema rules like primary keys, constraints, and relationships without writing separate configuration code, enabling EF Core to build the model at runtime.

Read full answer

Applies toEF Core 8 on .NET 8

What They Are

Data Annotations are declarative attributes applied to entity classes and properties to configure database mapping directly within the C# model. They live primarily in System.ComponentModel.DataAnnotations and System.ComponentModel.DataAnnotations.Schema, and EF Core reads them during model building to override default conventions. This keeps configuration close to the domain logic, reducing the need for separate Fluent API configuration for simple scenarios.

Key Attributes

  • [Key]: Marks a property as the primary key, overriding convention-based detection (e.g., Id or ClassNameId).

  • [Required]: Ensures the property cannot be null in the database, generating a NOT NULL column; it also enables ASP.NET Core model validation.

  • [StringLength]: Limits the maximum length of string properties, mapping to nvarchar(200) (or the specified size) instead of nvarchar(max).

  • [Column]: Specifies the exact database column name or data type, decoupling the C# name from the schema.

  • [Table]: Defines the database table name for the entity class.

  • [NotMapped]: Excludes a property from database mapping entirely.

  • [ForeignKey]: Explicitly defines navigation property relationships when conventions are ambiguous.

Configuration Precedence

EF Core uses a hierarchy when resolving model configurations. Understanding this prevents unexpected overrides: 1. Fluent API: Highest priority; explicitly configured in OnModelCreating. 2. Data Annotations: Middle priority; applied via attributes on the model. 3. Conventions: Lowest priority; default behaviors like naming Id as primary key.

If a setting is defined in both Fluent API and Data Annotations, the Fluent API setting wins. This allows developers to use annotations for common constraints while reserving complex logic for configuration classes.

Best Practices

  • Domain Purity: Avoid cluttering domain models with database concerns if they are shared across layers; use Fluent API in such cases.

  • Consistency: Choose one primary configuration method per project to maintain readability.

  • Validation: Remember that [Required] also enables ASP.NET Core model validation, coupling database rules to UI validation.

  • Complex Relationships: Use Fluent API for many-to-many relationships or complex indexes, as annotations have limited expressiveness there.

Data Annotations simplify initial development but should be evaluated against project architecture needs.

42

Can Entity Framework map query results directly to view-models, or is it restricted to working solely with database entity classes?

Entity Framework Core supports reading data directly into view-models via projection, but it cannot track or save changes using view-model classes themselves. You must define entity classes that map to database tables for persistence operations, while view-models serve as shaped data structures for the presentation layer.

Read full answer

Applies toEF Core 8 on .NET 8

Entities versus View-Models

Entities are plain C# classes configured via Fluent API or data annotations to map to database tables, columns, and relationships. They are tracked by the DbContext change tracker to enable SaveChanges and detect modifications. View-models are designed for specific UI requirements and often combine data from multiple entities or exclude sensitive fields. Mixing these concerns violates separation of concerns and can lead to over-posting vulnerabilities where clients modify fields they should not access.

Querying with Projection

To retrieve data into a view-model, use LINQ Select to project entity properties into the view-model constructor or object initializer. This generates SQL that selects only the required columns, reducing memory usage and network traffic. For read-only scenarios, always append AsNoTracking to prevent the context from tracking these instances, which improves performance and avoids concurrency issues.

Saving Data Limitations

EF Core cannot persist changes made to a view-model because it lacks mapping metadata and change tracking for those types. You must map the view-model back to an entity before calling Update or Attach. This mapping can be done manually or using a library like AutoMapper, but validation should occur before mapping to ensure data integrity.

Best Practices and Pitfalls

Register your context as scoped using AddDbContext in dependency injection to ensure one instance per request and avoid thread-safety issues. Use projection instead of Include when you only need specific fields, as Include loads entire entities and can cause N+1 problems if navigations are accessed later. Never expose database keys or passwords in view-models. Do not attempt to pass view-models directly into context.Update() or context.Add(), as this will result in runtime exceptions or unintended data loss. Always validate input before mapping it to an entity to prevent injection or state manipulation attacks. For bulk updates that do not require change tracking, prefer ExecuteUpdate over loading entities.

43

What approach ensures data consistency when a single operation must persist changes through multiple DbContext instances in Entity Framework Core?

To ensure consistency across multiple DbContext instances, you should manually share a single open DbConnection and enlist each context in the same explicit transaction, avoiding distributed transaction escalation when targeting a single database.

Read full answer

Applies toEF Core 8 on .NET 8

Shared Connection Strategy

When multiple contexts must commit atomically, create one DbConnection, open it, and begin a transaction manually. Pass this open connection into each DbContext via DbContextOptionsBuilder and call Database.UseTransaction() on every context instance. Follow these steps to implement the pattern:

  1. Instantiate and open a single SqlConnection using the shared connection string.

  2. Begin an explicit transaction on that connection using BeginTransaction().

  3. Configure each DbContext to use the open connection via UseSqlServer(connection).

  4. Enlist each context in the transaction by calling context.Database.UseTransaction(transaction).

This binds all contexts to the same local transaction rather than allowing each to open its own connection, which would require coordination via a distributed transaction coordinator.

TransactionScope vs Explicit Transactions

  • Explicit Connection Sharing: Preferred for multiple contexts against the same database. It avoids the overhead of Microsoft Distributed Transaction Coordinator (DTC) and gives fine-grained control over commit and rollback logic.

  • TransactionScope: Useful when contexts target different database servers or when wrapping non-database resources. However, opening multiple connections inside a TransactionScope often escalates to DTC, which adds latency and configuration complexity.

Feature Shared Connection TransactionScope
Same Database Optimal Works but heavier
Different Servers Not supported Supported
DTC Escalation No Likely
Complexity Moderate Low (initially)

Code Example

using Microsoft.EntityFrameworkCore;
using Microsoft.Data.SqlClient;
using System.Data;

public class MyContext : DbContext
{
    public MyContext(DbContextOptions<MyContext> options) : base(options) { }
}

// Use secure configuration management for connection strings in production
var connectionString = "Server=.;Database=MyDb;Trusted_Connection=True;Encrypt=True;";
using var connection = new SqlConnection(connectionString);
connection.Open();

using var transaction = connection.BeginTransaction();
try
{
    var options = new DbContextOptionsBuilder<MyContext>()
        .UseSqlServer(connection)
        .Options;

    using var context1 = new MyContext(options);
    context1.Database.UseTransaction(transaction);
    context1.SaveChanges();

    using var context2 = new MyContext(options);
    context2.Database.UseTransaction(transaction);
    context2.SaveChanges();

    transaction.Commit();
}
catch
{
    transaction.Rollback();
    throw;
}

Best Practices and Pitfalls

  • DbContext Lifetime: Normally register DbContext as Scoped via AddDbContext in DI. The manual instantiation shown above is a specific exception for transactional boundaries and should not replace standard Scoped registration elsewhere.

  • Connection State: Ensure the connection is open before passing it to UseSqlServer; otherwise, EF Core may attempt to open a new one, breaking the transaction enlistment.

  • Disposal: Always wrap contexts and transactions in using statements to guarantee resources are released, even if an exception occurs during processing.

  • Isolation Level: Specify the isolation level explicitly in BeginTransaction(IsolationLevel.ReadCommitted) if the default behavior causes excessive locking or deadlocks.

44

Is it possible to unit test code that depends on Entity Framework?

Unit testing EF code is possible, but the best approach depends on what you are testing. Business logic is usually tested by abstracting data access behind a repository or interface, while EF queries and mappings are best validated with integration tests against a real database.

Read full answer

Applies toEF Core 8 on .NET 8

Testing Strategies

  • In-memory provider: Microsoft.EntityFrameworkCore.InMemory gives fast tests without a database server, but it does not enforce relational constraints, transactions, or provider-specific SQL behavior.

  • Integration tests: Use a real database, often through Testcontainers or a local test instance, when query correctness, constraints, migrations, or provider behavior matter.

  • Abstraction over DbContext: Hide DbContext behind a repository or service interface so business rules can be tested with fakes or mocks without EF.

  • Mocking DbContext directly: It can work for simple cases, but async LINQ over DbSet<T> is awkward and tests often become brittle.

The choice is not either/or: a healthy test suite often combines fast in-memory unit tests for services with a smaller set of integration tests for the data layer.

When the In-Memory Provider Is Useful

Use it for fast, isolated tests of application code that uses EF Core APIs but does not depend on database-specific behavior. Give each test a unique database name so tests cannot share state. Do not rely on it to prove foreign keys, unique constraints, transaction rollback, or raw SQL behavior.

Comparing In-Memory and Integration Tests

Aspect In-Memory Provider Real Database (Integration)
Speed Fast, no server required Slower, requires a database server
Constraints Not enforced Fully enforced
Transactions Limited or no support Full support
SQL generation Not tested Validated against the real provider
Best for Business logic and service layer Queries, mappings, and migrations

Best Practices and Common Pitfalls

  • Register DbContext as Scoped with AddDbContext; never share one instance across threads or keep it as a Singleton.

  • Use AsNoTracking() for read-only queries to avoid unnecessary change tracking.

  • Use Include/ThenInclude or projections to prevent N+1 queries.

  • Use ExecuteUpdate and ExecuteDelete for bulk changes instead of loading entities into memory.

  • Do not treat in-memory tests as proof that production SQL will behave the same way.

  • Prefer integration tests for anything involving constraints, transactions, concurrency, or provider-specific SQL.

  • When using the in-memory provider, pass a unique database name per test (for example, a Guid) to avoid state leaking between tests.

45

What role does Entity Framework Core play within the modern .NET development stack and how is it integrated into applications built on this platform?

Entity Framework Core functions as the official object-relational mapper (ORM) for the .NET platform, enabling developers to interact with relational databases using .NET objects instead of raw SQL. It is deeply integrated into the ASP.NET Core dependency injection container and supports modern workflows such as code-first migrations and LINQ-based queries.

Read full answer

Applies toEF Core 8 on .NET 8

Dependency Injection Integration

In .NET 8, EF Core contexts are registered via the built-in dependency injection (DI) system. The recommended lifetime is Scoped, ensuring one context instance per HTTP request or logical operation. This prevents concurrency issues and memory leaks associated with Singleton contexts.

Querying and Change Tracking

Developers query data using LINQ, which translates expressions into SQL. By default, the context tracks changes to entities for automatic updates. For read-only scenarios, AsNoTracking improves performance by disabling the identity map. Relationships are eager-loaded using Include and ThenInclude to prevent N+1 query problems.

Schema Management with Migrations

EF Core manages database schema evolution through migrations. Developers define models in C# classes, then generate migrations via the CLI or Package Manager Console. These migrations create versioned SQL scripts that can be applied to development, staging, and production databases consistently.

Best Practices and Performance

  • Scoped Lifetime: Always register DbContext as Scoped using AddDbContext.

  • Read-Only Queries: Use AsNoTracking when data is not modified.

  • Bulk Operations: Use ExecuteUpdate and ExecuteDelete (EF Core 7+) for bulk changes without loading entities.

  • Raw SQL: Prefer FromSql with interpolated strings over FromSqlRaw to ensure parameters are bound correctly.

46

Can you explain the concept of dependency injection and describe the standard approach for integrating it with Entity Framework Core in a .NET application?

Dependency injection is a design pattern in which a class receives its dependencies from an external container rather than constructing them itself; with Entity Framework Core it is used to manage the DbContext lifecycle through the built-in .NET IoC container, so contexts are created and disposed per scope without manual instantiation.

Read full answer

Applies toEF Core 8 on .NET 8

How Dependency Injection Works

Inversion of Control means the framework constructs objects and supplies the services they request. When a controller or service asks for a DbContext, the container supplies the instance registered at startup. This removes tight coupling between data access and business logic, and makes unit testing easier because a mock context can be substituted for the real one.

Registering DbContext

The context is registered during startup with AddDbContext<TContext>. This configures the database provider (for example SQL Server via UseSqlServer) and, by default, registers the context with a Scoped lifetime. A scoped instance is shared inside one logical operation—such as a single HTTP request—and is disposed when the scope ends, which prevents stale state, memory leaks, and cross-thread concurrency problems.

Code Example

using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = null!;
}

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; } = null!;

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}

public class ProductService
{
    private readonly AppDbContext _context;

    public ProductService(AppDbContext context)
    {
        _context = context;
    }

    public List<Product> GetProducts()
    {
        return _context.Products.AsNoTracking().ToList();
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        // Requires the Microsoft.EntityFrameworkCore.SqlServer package.
        builder.Services.AddDbContext<AppDbContext>(options =>
            options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

        builder.Services.AddScoped<ProductService>();

        var app = builder.Build();
        app.Run();
    }
}

Best Practices and Common Pitfalls

  • Scoped Lifetime: Always register DbContext through AddDbContext; never as a Singleton, because it is not thread-safe and accumulates change-tracker state across requests.

  • Constructor Injection: Consume the context through a constructor parameter; do not resolve it manually from IServiceProvider inside application code.

  • Read-Only Queries: Use AsNoTracking when data is only displayed to avoid change-tracking overhead.

  • Avoid N+1 Queries: Use Include/ThenInclude or projections (Select) so related data is fetched in a single query.

  • Bulk Operations: Prefer ExecuteUpdate or ExecuteDelete (EF Core 7+) for bulk changes instead of loading entities first.

  • Match Service Lifetimes: A service that depends on a scoped DbContext should itself be scoped; a singleton service must not depend on a scoped context.

  • Configuration Security: Store connection strings in user secrets, environment variables, or a secret manager—never hard-code them in source.

47

How does Entity Framework Core support asynchronous operations in .NET applications?

EF Core exposes asynchronous query, persistence, and bulk-operation methods that return Task or IAsyncEnumerable, letting database I/O run without blocking request threads. Each DbContext instance remains non-thread-safe and should be used for one operation at a time within a scoped lifetime.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

  • Query methods such as ToListAsync, FirstOrDefaultAsync, CountAsync, and AnyAsync execute the database call asynchronously.

  • SaveChangesAsync persists tracked entity changes and, by default, wraps them in a transaction.

  • AsAsyncEnumerable streams results without loading the whole result set into memory.

  • BeginTransactionAsync, CommitAsync, and RollbackAsync support asynchronous transaction control.

  • ExecuteUpdateAsync and ExecuteDeleteAsync perform set-based bulk changes without loading entities first.

  • Pass a CancellationToken through async calls so requests can be cancelled cleanly.

Concurrency and Lifetime

  • Register the context with AddDbContext, giving each request or scope its own instance.

  • Never share one DbContext across threads or use it as a singleton.

  • Do not run multiple queries concurrently on the same context; await each operation before starting the next.

  • Use AsNoTracking for read-only queries to reduce tracking overhead.

  • Use Include/ThenInclude or projections to avoid N+1 query patterns.

  • Avoid lazy loading in async request paths because it can trigger synchronous database access; prefer eager loading.

Code Example

using Microsoft.EntityFrameworkCore;

public sealed class OrderRepository(AppDbContext context)
{
    public async Task<List<Order>> GetRecentOrdersAsync(
        int customerId,
        CancellationToken cancellationToken = default)
    {
        return await context.Orders
            .AsNoTracking()
            .Where(o => o.CustomerId == customerId)
            .OrderByDescending(o => o.CreatedAt)
            .Take(20)
            .ToListAsync(cancellationToken);
    }

    public Task<int> CancelStalePendingOrdersAsync(
        DateTime cutoff,
        CancellationToken cancellationToken = default) =>
        context.Orders
            .Where(o => o.Status == OrderStatus.Pending && o.CreatedAt < cutoff)
            .ExecuteUpdateAsync(
                s => s.SetProperty(o => o.Status, OrderStatus.Cancelled),
                cancellationToken);
}

Best Practices and Pitfalls

  • Always await async EF Core calls; do not block with .Result or .Wait().

  • Pass CancellationToken to query and save methods.

  • Keep DbContext scoped and short-lived.

  • Use AsNoTracking for reads that do not modify entities.

  • Prefer ExecuteUpdateAsync and ExecuteDeleteAsync for bulk changes.

  • Avoid mixing sync and async database access on the same context.

48

What strategies and tools do you employ to diagnose and fix performance bottlenecks in Entity Framework Core applications?

Troubleshooting Entity Framework performance relies on diagnostic logging and query inspection to reveal the actual SQL sent to the database and identify inefficiencies like excessive round trips or missing indexes. By capturing execution details and analyzing query patterns, developers can pinpoint whether the bottleneck lies in the application logic, the ORM configuration, or the database schema.

Read full answer

Applies toEF Core 8 on .NET 8

Enabling Diagnostic Logging

The most effective first step is configuring the DbContext to log generated SQL commands and execution events. In EF Core 8, use the LogTo method within DbContextOptionsBuilder to route logs to a console, debugger, or file provider. This output includes the SQL statement and execution duration; parameter values are logged only when EnableSensitiveDataLogging is enabled, so use that flag carefully. Ensure logging is disabled or filtered in production to avoid performance overhead and sensitive data exposure.

Identifying Common Bottlenecks

Performance issues typically stem from specific anti-patterns. The N+1 query problem occurs when related data is loaded lazily in a loop; resolve this using Include or ThenInclude for eager loading, or projections via Select. Excessive change tracking slows down read-only operations; apply AsNoTracking to queries that do not update entities. For bulk modifications, avoid retrieving entities just to save them; use ExecuteUpdate and ExecuteDelete (introduced in EF Core 7) to perform operations directly in the database.

Inspecting Generated SQL

During development, you can inspect the SQL for a specific LINQ query before execution using ToQueryString(). This helps verify that filters are translated correctly into WHERE clauses rather than being applied client-side. When raw SQL is necessary, always use parameterized APIs: prefer FromSqlInterpolated and ExecuteSqlInterpolated (EF Core 5+) because they automatically treat interpolated values as parameters; never concatenate user input into FromSqlRaw or ExecuteSqlRaw.

Best Practices and Pitfalls

Register your DbContext as Scoped using AddDbContext; never share a single instance across threads or store it as a Singleton, as this causes concurrency issues and memory leaks. Index database columns used in Where, Join, and OrderBy clauses to prevent table scans. Be cautious with EnableSensitiveDataLogging, as it logs parameter values including potentially sensitive information; restrict this to development environments only. Finally, regularly review execution plans in your database engine to ensure indexes are being utilized effectively by the generated queries.

49

What are the frequently encountered errors in Entity Framework, and what strategies exist to troubleshoot and resolve them?

Entity Framework Core surfaces a small set of recurring runtime errors—concurrency conflicts, context-lifetime violations, query-translation failures, and constraint violations—each with a recognizable exception type and a standard remediation pattern. Mastering exception handling for these cases keeps data integrity intact and avoids cascading failures.

Read full answer

Applies toEF Core 8 on .NET 8 (SQL Server provider where noted)

Concurrency Conflicts

  • Exception: DbUpdateConcurrencyException.

  • Cause: An update or delete affects zero rows because a concurrency token (for example, a [Timestamp] rowversion column) changed after the entity was loaded.

  • Resolution: Reload the entity with entry.GetDatabaseValues(), merge the changes into OriginalValues, and retry SaveChangesAsync; alternatively apply a retry policy with exponential backoff for transient collisions.

  • Prevention: Mark a property with [Timestamp] or [ConcurrencyCheck] to enable optimistic concurrency; avoid long-lived contexts that hold stale data.

Context Lifecycle Issues

  • Exception: InvalidOperationException (for example, “The operation cannot be completed because the DbContext has been disposed” or “A second operation started on this context before a previous operation completed”).

  • Cause: Using a singleton or disposed DbContext, accessing it from multiple threads, or enumerating a query after the request scope ends.

  • Resolution: Register the context as Scoped with AddDbContext; never share one instance across threads; materialize results with ToList() before the scope ends.

  • Platform note: Setting MultipleActiveResultSets=True in a SQL Server connection string can mask open-reader errors, but it is a workaround rather than a fix—prefer separate connections or eager loading.

Query Translation Failures

  • Exception: InvalidOperationException. EF Core 8 translates LINQ to SQL and throws when a method or expression has no SQL equivalent; client evaluation is permitted only in the final projection.

  • Cause: Using unsupported .NET methods, mixing client and server logic, or calling ToString() or DateTime methods that the provider cannot map.

  • Resolution: Rewrite the query with translatable constructs (EF.Functions.Like, DateTime primitives), push client-side work after AsEnumerable(), or move the logic to a computed column or database view.

  • Tip: Enable detailed logging to inspect the generated SQL and identify the exact untranslated fragment.

Database Constraint and Connection Errors

  • Exception: DbUpdateException (wrapping a SqlException) for unique-key, foreign-key, or NOT NULL violations; SqlException for timeouts, deadlocks, or connectivity loss.

  • Cause: Business-rule violations that reach the database, or transient network and locking problems.

  • Resolution: Validate input before saving; inspect the inner exception to identify the failing constraint; apply retry logic for deadlocks and timeouts; keep transactions short.

Best Practices

  • Scoped Lifetime: DbContext is not thread-safe; register it as Scoped and let the DI container dispose it.

  • Parameterized Raw SQL: Use FromSqlInterpolated / ExecuteSqlInterpolated; never build SQL by string concatenation.

  • Read-Only Queries: Apply AsNoTracking() to queries that only read data.

  • Bulk Changes: Use ExecuteUpdate / ExecuteDelete (EF Core 7+) to avoid loading entities.

  • Logging: Log exceptions with EnableSensitiveDataLogging only in development; capture the SQL that caused a translation failure.

  • Migrations: Manage schema with Add-Migration / Update-Database rather than hand-editing the database.

50

What steps and tools do you use to diagnose and resolve problems with Entity Framework Core migrations?

Troubleshooting EF Core migrations usually means identifying whether the problem is a pending model change, a stale migration history, a team merge conflict, or schema drift between the database and the model snapshot. The fix depends on which of these is true, so diagnosis comes first.

Read full answer

Applies toEF Core 8 on .NET 8

Common Symptoms

  • dotnet ef database update reports pending model changes or fails on a specific migration.

  • dotnet ef migrations add creates an unexpectedly empty or huge migration.

  • Two developers’ migrations conflict after a merge, often in *.Designer.cs or ModelSnapshot.cs.

  • The database schema no longer matches the EF model because changes were made directly in SQL.

Diagnostic Commands

  • dotnet ef migrations list shows applied vs. pending migrations.

  • Inspect the __EFMigrationsHistory table to see exactly which migrations the database thinks are applied.

  • dotnet ef dbcontext info confirms which context and provider are being used.

  • Compare the generated migration’s Up/Down methods against the actual schema change you expected.

Resolving Team Conflicts

  • When two migrations share a timestamp prefix, remove the later one with dotnet ef migrations remove, update the database to the common ancestor, then re-add the migration so it chains correctly.

  • Never hand-edit migration timestamps to “fix” ordering; regenerate instead.

  • After resolving, run dotnet ef database update and verify with dotnet ef migrations list.

Handling Database Drift

  • If the database was changed outside EF, do not force a migration over it blindly. Either revert the manual change or scaffold the current schema with dotnet ef dbcontext scaffold and reconcile.

  • For an existing database being brought under EF control, create a baseline migration whose Up/Down are empty but whose snapshot captures the current model, then mark it applied in __EFMigrationsHistory.

Best Practices and Pitfalls

  • Always review generated migrations before applying; EF Core cannot always infer renames and may emit drop-and-recreate.

  • Keep migrations small and frequent to make conflicts and rollbacks manageable.

  • Use dotnet ef migrations script to produce idempotent SQL for production deployments instead of running the CLI against the database.

  • A failed migration normally rolls back within its transaction, but some DDL cannot be rolled back on certain providers, so test migrations against a copy of production data first.

Code Example

# Inspect state
dotnet ef migrations list

# Add a migration after model changes
dotnet ef migrations add AddOrderStatus

# Apply pending migrations
dotnet ef database update

# Generate an idempotent SQL script for deployment
dotnet ef migrations script --idempotent -o migrate.sql

# Remove the last unapplied migration
dotnet ef migrations remove
52

What best practices should you follow to handle database connection strings securely within an Entity Framework Core application?

To manage connection strings securely in Entity Framework Core, avoid hardcoding credentials and instead use the .NET configuration hierarchy, prioritizing User Secrets locally and Azure Key Vault or environment variables in production. This keeps sensitive values out of source control and injects them at runtime.

Read full answer

Applies toEF Core 8 on .NET 8

Secure Storage Mechanisms

Never store connection strings directly in code files such as appsettings.json when they contain passwords. Instead, use these layered options: - User Secrets: Stored outside the project tree in a JSON file specific to the user profile, ideal for development. - Environment Variables: Set on the host machine or container orchestration platform, suitable for staging and production. - Azure Key Vault: A cloud-based secure store accessed via managed identity, recommended for Azure-hosted applications to avoid credential leakage.

Configuration Strategy

The application retrieves the string through the configuration builder and registers the context with a scoped lifetime. This ensures each request gets its own context instance, preventing thread-safety issues. Configuration sources are loaded in a specific order where later sources override earlier ones, allowing secure values to override defaults safely.

Code Example

The following snippets demonstrate retrieving the connection string from secure configuration and registering the DbContext correctly.

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Retrieves the connection string from secure configuration sources
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? throw new InvalidOperationException("Connection string 'DefaultConnection' was not found.");

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString),
    ServiceLifetime.Scoped);
using Microsoft.EntityFrameworkCore;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }
}

Common Security Pitfalls

  • Hardcoding: Embedding strings in source code leads to accidental exposure through repositories.

  • Logging Connection Strings: Never log the full connection string; mask passwords if debugging is required.

  • Singleton DbContext: Avoid registering DbContext as a Singleton, as it is not thread-safe and can cache sensitive state across requests.

  • Source Control Leaks: Ensure .gitignore excludes secrets.json and configuration files containing credentials.

By adhering to these practices, you maintain a secure separation between code and credentials while ensuring the DbContext operates within its intended lifecycle.

53

Which architectural patterns and performance strategies are advised when building Entity Framework Core solutions for high-volume enterprise systems?

Building high-volume enterprise systems with Entity Framework Core centers on scalable architecture and performance optimization: keep DbContext lifetimes short, eliminate redundant database round-trips, and use set-based bulk APIs instead of materializing large entity graphs.

Read full answer

Applies toEF Core 8 on .NET 8

Core Architecture and DbContext Lifetime

  • Register DbContext as scoped with AddDbContext so one instance serves each logical request or unit of work.

  • Never register DbContext as a singleton or share it across threads; it is not thread-safe and its change-tracking cache will grow without bound.

  • Split the system into bounded contexts, each with its own focused DbContext, to keep aggregates, transaction boundaries, and migrations manageable.

  • Apply CQRS for read-heavy workloads: a write DbContext with full change tracking, plus a read-only DbContext that uses AsNoTracking and projections.

  • Use the outbox pattern when publishing domain events: store events in the same database transaction, then relay them asynchronously, avoiding distributed transactions.

Query Performance and Data Loading

  • Eager load related data with Include and ThenInclude when it is truly needed, but beware cartesian products when multiple collections are included.

  • Use AsSplitQuery (EF Core 5+) to load multiple collections in separate SQL statements instead of one wide join.

  • Mark read-only queries with AsNoTracking(); use AsNoTrackingWithIdentityResolution() when duplicate rows must be merged without full tracking.

  • Project to DTOs with Select so only required columns cross the network.

  • Cache frequently executed query shapes with EF.CompileAsyncQuery, but avoid compiling unique parameter combinations to prevent memory growth.

  • Never iterate a parent collection and query children inside the loop; that is the classic N+1 anti-pattern.

Bulk Operations and Set-Based Updates

  • Use ExecuteUpdateAsync and ExecuteDeleteAsync (EF Core 7+) for mass changes; they translate to single SQL statements and bypass change tracking.

  • Batch inserts with AddRange followed by one SaveChangesAsync; EF Core batches commands where the provider supports it.

  • Avoid retrieving thousands of rows just to modify one property; materialization, not the update itself, is usually the memory bottleneck.

Safe Raw SQL and Diagnostics

  • Always parameterize raw SQL through FromSqlInterpolated or ExecuteSqlInterpolated; interpolated variables become SQL parameters.

  • Never concatenate user input into FromSqlRaw or ExecuteSqlRaw, which invites SQL injection.

  • Capture generated SQL and execution times with LogTo or standard logging providers during development; keep production logging at a sustainable level.

  • Enable EnableRetryOnFailure for transient faults in cloud or distributed database environments.

Best Practices and Common Pitfalls

  • Prefer async APIs such as ToListAsync and SaveChangesAsync to prevent thread-pool starvation under load.

  • Define indexes on columns used in Where, Join, and OrderBy clauses, using HasIndex in migrations.

  • Keep transactions short; long-running transactions increase locking, blocking, and deadlock risk.

  • Version-control migrations and run them in CI/CD against a schema representative of production to prevent drift.

  • Avoid a generic repository wrapper around DbContext unless you genuinely need to swap data sources; it often obscures EF Core features.

  • Consider AddDbContextPool only after measuring; pooling can leak state if contexts hold event handlers or scoped services.

55

What common pitfalls and discouraged patterns should developers avoid when building applications with Entity Framework Core?

When working with Entity Framework Core, developers must avoid anti-patterns such as treating the DbContext as a singleton, executing unparameterized raw SQL, and triggering N+1 query problems. Adhering to scoped lifetimes and using built-in bulk operations ensures performance and security.

Read full answer

Applies toEF Core 8 on .NET 8

DbContext Lifetime Management

Never register the DbContext as a Singleton or share it across threads. A DbContext instance is not thread-safe and holds change tracking state that grows indefinitely if kept alive too long. Instead, register it as Scoped using AddDbContext in the dependency injection container. This ensures a new instance per request in web applications, preventing memory leaks and concurrency exceptions. Manually disposing a DbContext that is managed by DI can also cause errors; let the container handle disposal at the end of the scope.

Query Efficiency and N+1 Issues

Avoid loading related data implicitly inside loops, which causes the N+1 query problem. For example, iterating over a list of orders and calling order.Customer for each one issues a separate query per order. Use Include or ThenInclude to eager load relationships in a single query. For read-only scenarios, append AsNoTracking to disable change tracking overhead. If only specific fields are needed, use projections with Select rather than fetching entire entities. Always prefer async variants such as ToListAsync and FirstOrDefaultAsync in web applications to free threads during I/O.

Raw SQL Security

Never concatenate user input into FromSqlRaw or ExecuteSqlRaw, as this invites SQL injection attacks. For instance, building a query string with string.Format or interpolation into FromSqlRaw is unsafe. EF Core provides FromSqlInterpolated and ExecuteSqlInterpolated, which automatically parameterize inputs. Still, prefer LINQ whenever possible: context.Products.Where(p => p.Category == category) is safer and more maintainable than raw SQL. When raw SQL is necessary, rely on interpolation and never log sensitive parameter values in production.

Bulk Operations Efficiency

Avoid retrieving thousands of entities just to modify a single property. Loading entities into memory triggers change tracking and consumes significant resources. A common mistake is to call ToList(), loop over the results, modify each entity, and then call SaveChanges(). Instead, use ExecuteUpdate and ExecuteDelete (introduced in EF Core 7) to perform bulk changes directly in the database without materializing entities. For example, context.Products.ExecuteUpdateAsync(setters => setters.SetProperty(p => p.Price, p => p.Price + increase)) updates all matching rows in one command.

Common Pitfalls and Best Practices

  • Disposing Contexts: Do not manually dispose DbContext instances managed by DI; let the container handle disposal at the end of the scope.

  • Async Usage: Always use async variants (ToListAsync, SaveChangesAsync) in web applications to free up threads during I/O.

  • Logging: Enable logging to detect slow queries, but ensure sensitive parameter values are not logged in production environments.

  • Migrations: Avoid editing generated migration files manually; create new migrations for schema changes to maintain history integrity.

  • Change Tracking Overhead: Use AsNoTracking for read-only queries; otherwise EF Core keeps snapshots of every entity, increasing memory usage.

  • Lazy Loading: Enable lazy loading only when necessary and be aware that it can cause unexpected queries; prefer explicit loading or eager loading with Include.

56

What mechanisms does Entity Framework Core employ to safeguard applications against SQL injection vulnerabilities?

Entity Framework Core safeguards applications by automatically generating parameterized queries for LINQ operations, and by offering interpolated raw-SQL APIs that bind variables as parameters instead of concatenating them into SQL text.

Read full answer

Applies toEF Core 8 on .NET 8

Default LINQ Behavior

LINQ queries are translated into SQL by the query pipeline. Every variable captured in the query expression is passed as a DbParameter. This means user input used in Where, OrderBy, or Select clauses is never concatenated into the command text. For example, context.Users.Where(u => u.Name == userInput) generates SQL where userInput is bound safely, regardless of its content.

Raw SQL Execution

EF Core 8 offers two approaches for raw SQL. FromSqlRaw and ExecuteSqlRaw require explicit parameter arguments with positional {0} placeholders, while FromSqlInterpolated and ExecuteSqlInterpolated automatically convert interpolated variables into parameters. The interpolated versions are preferred for readability and safety. Note that parameters cannot represent identifiers like table or column names; those must be validated against a whitelist if dynamic.

Unsafe Practices to Avoid

Never concatenate user input into raw SQL strings. Even with EF Core, passing concatenated strings to FromSqlRaw bypasses parameterization. Additionally, avoid dynamic table or column names via parameters, as SQL parameters cannot represent identifiers. Using string interpolation to build SQL that is then passed to FromSqlRaw is a common mistake that reintroduces injection risks.

Best Practices and Pitfalls

  • DbContext Lifetime: Always register DbContext as Scoped via AddDbContext in dependency injection. Never share it across threads or store it as a Singleton.

  • Read-Only Queries: Use AsNoTracking() for read-only operations to improve performance and avoid unintended change tracking.

  • Defense in Depth: Parameterization stops injection, but validate input for business logic correctness (e.g., length limits).

  • Bulk Operations: Use ExecuteUpdate and ExecuteDelete (EF Core 7+) for bulk changes instead of fetching entities first, ensuring parameters are still used.

  • Raw SQL Caution: Prefer LINQ over raw SQL. If raw SQL is unavoidable, use interpolated methods exclusively over raw string concatenation.

57

What security measures should be implemented when using Entity Framework Core to handle data access for concurrent users in a shared application environment?

In a multi-user environment, the primary security focus when using Entity Framework Core is preventing SQL injection while ensuring strict data isolation between users. This requires relying on LINQ parameterization, configuring scoped DbContext instances, and applying global query filters to enforce tenant or user boundaries automatically.

Read full answer

Applies toEF Core 8 on .NET 8

Preventing SQL Injection

Entity Framework Core automatically parameterizes LINQ queries, making them safe by default. The significant risk arises when executing raw SQL via FromSqlRaw or ExecuteSqlRaw. Concatenating user input into these methods allows attackers to inject malicious commands. Always use FromSqlInterpolated or ExecuteSqlInterpolated, which detect interpolated variables and convert them into SQL parameters. Never pass raw strings constructed from user input. Prefer LINQ methods over raw SQL whenever possible to eliminate this risk entirely.

Enforcing Data Isolation

In multi-tenant or multi-user systems, users must only access their own data. Hardcoding filters in every query is error-prone and easily forgotten. Instead, configure a global query filter in OnModelCreating using HasQueryFilter. This automatically appends a predicate (e.g., TenantId == currentTenant) to all queries, including ExecuteUpdate and ExecuteDelete operations. This ensures isolation even if a developer forgets to add a Where clause in a specific endpoint, preventing cross-user data leakage.

Managing Concurrency Conflicts

When multiple users edit the same record simultaneously, lost updates can occur. EF Core supports optimistic concurrency control to handle this. Mark a property with the [Timestamp] attribute or configure it using IsConcurrencyToken(). When SaveChanges is called, EF includes the original value in the WHERE clause. If another user modified the row, the update affects zero rows, and EF throws a DbUpdateConcurrencyException, allowing the application to resolve the conflict rather than silently overwriting data.

Best Practices and Pitfalls

  • Scoped Lifetime: Register DbContext as Scoped via AddDbContext in dependency injection. Never use Singleton, as it causes thread-safety issues and data leakage between requests.

  • Sensitive Data: Do not store secrets in plain text. Use Always Encrypted or application-level encryption before saving to the database.

  • AsNoTracking: Use AsNoTracking() for read-only queries to reduce memory overhead and prevent accidental modifications to tracked entities.

  • Bulk Operations: Use ExecuteUpdate and ExecuteDelete (EF Core 7+) for bulk changes instead of loading entities into memory, which reduces attack surface and improves performance.

  • Input Validation: Validate all inputs before passing them to EF, even though parameters are safe, to ensure business logic constraints are met.

58

What techniques can you use to protect confidential information stored in Entity Framework models?

Sensitive data in EF Core models is protected by combining encryption at rest (value converters or database-level TDE), row-level access control through global query filters, and strict control over what EF logs and exposes.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

EF Core does not encrypt data by itself; protection comes from layers around it. At the database level, features such as SQL Server Transparent Data Encryption (TDE) encrypt the whole data file. At the application level, value converters transform a property’s value before it is written and after it is read, letting you store ciphertext while working with plaintext in code. Global query filters enforce row-level security so users only see rows they are authorized to see. Finally, EF’s logging must be configured so that parameter values and entity contents are never written to logs in production. Because EF Core tracks changes and logs commands, a misconfigured logger can leak secrets even when the database is encrypted.

Key Techniques

  • Value converters for column-level encryption: implement ValueConverter<TModel, TProvider> to encrypt on write and decrypt on read, keeping the key outside the database (for example in Azure Key Vault or environment variables). This is useful for individual high-sensitivity columns such as SSNs or credit card numbers.

  • Database-level encryption (TDE): encrypts data files and backups transparently; combine with column-level encryption for defense in depth. TDE protects against physical file theft but not against a compromised application account that can read rows.

  • Row-level security via query filters: HasQueryFilter restricts queries to the current tenant or user, preventing cross-tenant data leakage. The filter is applied to all queries on the entity unless explicitly ignored.

  • Disable sensitive data logging: EnableSensitiveDataLogging(false) in production so parameter values are not captured in logs. By default, EF Core logs parameter values only when sensitive data logging is enabled, but verify your logger provider does not capture them independently.

  • Secrets management: never store connection strings or keys in source code; use dotnet user-secrets, environment variables, or a vault. In production, use managed identities or certificate-based authentication where possible.

  • Projection and DTOs: select only the columns a screen needs instead of materializing full entities containing secrets. This reduces the chance that a secret is accidentally serialized to a client or written to a log.

Comparison of Encryption Approaches

Approach Scope Key Management Performance Best For
Value converter Single column Application-side, e.g. Key Vault Per-row CPU overhead Highly sensitive fields, regulatory requirements
Database TDE Entire data file Database engine Minimal Physical theft, backups
Always Encrypted (SQL Server) Column Client driver Moderate End-to-end encryption, DBAs cannot read

Best Practices and Common Pitfalls

  • Keep encryption keys out of the database and out of source control; rotate them through a vault.

  • EnableSensitiveDataLogging is useful in development but must be off in production.

  • Value converters run per property; for large datasets consider database-level encryption instead.

  • Query filters are applied automatically, but IgnoreQueryFilters() bypasses them, so audit its use.

  • AES keys must be 16, 24, or 32 bytes; validate length at startup rather than failing at runtime.

  • Never concatenate user input into FromSqlRaw; use FromSqlInterpolated with parameters.

  • When using value converters, remember that indexed or filtered indexes cannot be used on the encrypted column because the stored value is ciphertext.

59

How does Entity Framework Core handle data encryption, and what implementation responsibilities fall on the developer to ensure sensitive fields are stored securely?

Entity Framework Core does not provide built-in property or column encryption; developers must implement application-level encryption themselves or configure database-level encryption, using EF Core value converters to translate between plaintext and ciphertext while keeping cryptographic logic out of the domain model.

Read full answer

Applies toEF Core 8 on .NET 8 (application-level pattern applies broadly; database features depend on the provider, e.g. SQL Server)

EF Core’s Native Capabilities

EF Core is an object-relational mapper, not a cryptographic framework. It does not encrypt data by default, so sensitive fields such as social security numbers, credit card numbers, or health records are stored as plain text unless the developer transforms them. Transport security such as TLS protects data in transit but does not protect data at rest in database files, backups, or logs. EF Core does, however, expose extension points—mainly value converters and interceptors—that let developers insert encryption and decryption during persistence and materialization.

Implementing Encryption with Value Converters

The standard application-level pattern is to register a ValueConverter in OnModelCreating for each sensitive property. The converter encrypts the value before it is written to the database and decrypts it when the entity is materialized. This keeps the domain model unchanged and centralizes the transformation in the data layer. Because encryption and decryption run on every read and write, they add CPU overhead and can become a bottleneck in high-throughput services, so the impact should be benchmarked.

Database-Level Encryption Alternatives

For stronger protection, database-native features are usually preferable: - Transparent Data Encryption (TDE) encrypts the whole database file and backups with minimal application changes, but it does not protect data from a database administrator or a compromised database process. - SQL Server Always Encrypted keeps encryption keys outside the database engine, so the server never sees plaintext. This reduces the attack surface but restricts querying: encrypted columns generally cannot be indexed, searched, or joined unless deterministic encryption and secure enclaves are configured.

Developer Responsibilities

Because EF Core does not manage encryption, the developer is responsible for: - Key management: Never hardcode keys in source code. Use a secrets manager such as Azure Key Vault, AWS KMS, or HashiCorp Vault, and inject them through dependency injection. - Algorithm choice: Use authenticated encryption such as AES-GCM. Avoid legacy algorithms such as DES, RC4, or AES in ECB mode. - Key rotation: Design the schema and conversion logic so that keys can be rotated without a full data outage. - Logging and diagnostics: Ensure plaintext values never appear in application logs, EF Core logging, or telemetry. EF Core does not log parameter values by default, but EnableSensitiveDataLogging can expose them. - DbContext lifetime: Register the DbContext as scoped with AddDbContext; never share it across threads or store it as a singleton. - Query performance: Use AsNoTracking for read-only queries and Include/ThenInclude or projections to avoid N+1 patterns, especially when decryption is expensive.

Comparison of Approaches

Approach Protection scope Query support Key exposure
Application-level encryption Column-level, application-controlled Limited; usually equality only Keys reachable by application code
TDE Entire database, files, backups Unchanged Managed by database engine
Always Encrypted Selected columns Restricted without enclaves Never reaches database engine

Best Practices and Common Pitfalls

  • Encrypt only fields that require it; over-encryption harms performance and complicates debugging.

  • Store authentication tags, initialization vectors, and algorithm identifiers alongside the ciphertext so future migrations remain possible.

  • Test decryption failures explicitly; a corrupted ciphertext should surface as a controlled exception rather than silently returning garbage.

  • If raw SQL is ever needed, use parameterized FromSql/ExecuteSql with interpolated parameters; never concatenate user input into FromSqlRaw.

  • Remember that application-level encryption does not replace backups, access control, or network security; it is one layer of a defense-in-depth strategy.

60

What key factors should be accounted for when integrating Entity Framework Core with Azure SQL Database in a production environment?

When integrating Entity Framework Core with Azure SQL Database in production, the dominant concerns are handling transient cloud faults through execution strategies, keeping DbContext instances short-lived and scoped, securing credentials with managed identities or Key Vault, and optimizing queries for higher latency and throttling limits.

Read full answer

Applies toEF Core 8 on .NET 8

Connection Resiliency

Azure SQL Database returns transient errors under resource governance, failover, or network instability, so EF Core must be configured to retry automatically rather than surface every glitch to the user. - Transient faults: Errors such as 40197, 40501, 10928, and 10929 are detected by the SQL Server execution strategy and retried when EnableRetryOnFailure is enabled. - Default retry policy: EnableRetryOnFailure() uses 6 retries with a 30-second maximum delay and exponential backoff; adjust maxRetryCount and maxRetryDelay based on operation criticality. - Timeouts: Command timeouts are not retried by default; error number -2 can be added to errorNumbersToAdd, but only for idempotent commands to avoid duplicate writes. - Command timeout: Set CommandTimeout through UseSqlServer options or the connection string for long-running reports, but prefer query tuning over very large timeouts.

DbContext Lifetime and Authentication

A DbContext is not thread-safe and should model a single unit of work, which matters especially when cloud calls take longer than on-premises calls. - Scoped registration: Register the context with AddDbContext so it is created per request; never store it as a singleton or share it across threads. - Azure AD authentication: Prefer Authentication=Active Directory Managed Identity or Authentication=Active Directory Default in the connection string instead of embedded SQL credentials. - Secret storage: Keep connection strings in Azure Key Vault or App Configuration and use private endpoints to keep traffic off the public internet. - Long-lived contexts: For background services or repeated operations, use IDbContextFactory<TContext> rather than injecting a long-lived DbContext.

Query and Throughput Optimization

Cloud latency makes round trips expensive, so EF Core usage should minimize data movement and avoid patterns that multiply requests. - Read-only queries: Use AsNoTracking to skip change tracking overhead and reduce memory pressure. - Related data: Use Include/ThenInclude or projections to prevent N+1 query storms across a higher-latency link. - Bulk changes: Use ExecuteUpdate/ExecuteDelete (EF Core 7+) to modify rows directly without materializing entities. - Payload size: Project only required columns with Select and avoid SELECT *. - Batching: Split large imports into batches; Azure SQL can throttle or block long transactions.

Capacity, Throttling, and Operations

EF Core cannot bypass database-tier limits, so application design must respect the Azure SQL compute and connection model. - Compute tier: Understand DTU or vCore limits and elastic-pool boundaries; throttling appears as transient errors but is best avoided through query and batch design. - Connection pooling: ADO.NET pooling is enabled by default; tune Max Pool Size to stay under the Azure SQL connection limit. - Migrations: Apply migrations from scripts or controlled Database.Migrate() calls during deployments; avoid automatic migrations in production. - Monitoring: Send EF Core logs to Azure Monitor and use Query Performance Insight to detect parameter-sensitive or missing-index plans.

Raw SQL and Injection Safety

Raw SQL remains useful for complex reporting or bulk operations, but user input must never be concatenated into command text. - Interpolated raw SQL: FromSqlInterpolated binds interpolated parameters safely and is the preferred way to pass values. - Explicit raw SQL: FromSqlRaw and ExecuteSqlRaw require explicit parameters such as SqlParameter when accepting user input. - Never concatenate: Building SQL strings from user input defeats parameterization and creates SQL injection risks.

Common Pitfalls

  • Retrying non-idempotent operations: Retries can duplicate inserts or side effects; design commands to be idempotent or scope retries carefully.

  • Large transactions: Long transactions increase blocking and throttling risk; keep transactions short and batch work.

  • Lazy loading: Disable it in high-throughput APIs because each access can trigger an unexpected round trip.

  • Sensitive data logging: Disable detailed EF Core parameter logging in production or mask values to prevent secrets from appearing in logs.

61

Does Entity Framework Core support Azure Cosmos DB as a database provider?

Yes, Entity Framework Core supports Azure Cosmos DB through the official EF Core Cosmos DB provider, which maps DbContext entities to JSON documents in Cosmos DB containers and translates LINQ queries to the Cosmos SQL API. It is a separate provider from the relational providers and has NoSQL-specific behavior and limitations.

Read full answer

Applies toEF Core 8 on .NET 8 (Microsoft.EntityFrameworkCore.Cosmos 8.x)

Provider Overview

  • The provider is distributed as the Microsoft.EntityFrameworkCore.Cosmos NuGet package and targets the Azure Cosmos DB SQL API.

  • No database schema is required; containers are created from the entity model configuration.

  • Entities are serialized to JSON documents, each with an id property and a partition key.

  • The provider supports familiar DbContext patterns, but document structure, partitioning, and request units must be considered.

Configuration and Registration

  • Register the DbContext with a scoped lifetime using AddDbContext; never share it as a singleton.

  • Configure the connection with UseCosmos, supplying the account endpoint, an authentication key or Azure TokenCredential, and the database name.

  • Keep credentials out of source control by using Azure Key Vault, developer user secrets, or managed identities.

  • Use ToContainer to map an entity to a specific container and HasPartitionKey to define the partition key.

Key Limitations

  • Cross-container joins are not supported; related data should be modeled as owned/embedded entities or queried separately.

  • Transactions are limited to a single container and partition key.

  • Not all LINQ operators translate to Cosmos SQL; unsupported queries may fall back to client evaluation or throw.

  • Aggregation and grouping queries should be tested to confirm they execute server-side.

  • Partition key design strongly affects Request Unit cost, latency, and scalability.

Best Practices

  • Scoped DbContext: use AddDbContext so each request or operation scope gets its own context.

  • Partition keys: choose high-cardinality keys and configure them explicitly with HasPartitionKey.

  • Read-only queries: use AsNoTracking to avoid document-tracking overhead.

  • Owned entities: embed aggregates inside a single document to avoid cross-document lookups.

  • Cost monitoring: track Request Units during development and use query diagnostics for expensive operations.

  • Authentication: prefer managed identities or token credentials over long-lived keys in production.

62

What strategies can be used to ensure Entity Framework scales effectively when deployed on Azure?

Managing scalability with Entity Framework on Azure requires careful configuration of context lifetime, connection management, and query optimization to handle increased load without degradation. Scalability in this context refers to the application’s ability to maintain performance as user demand grows, leveraging Azure’s cloud infrastructure alongside EF Core’s efficient patterns.

Read full answer

Applies toEF Core 8 on .NET 8, deployed on Azure App Service with Azure SQL Database

Dependency Injection and Context Lifetime

  • Scoped registration: Register the DbContext as scoped using AddDbContext so each HTTP request gets its own context instance. This prevents thread-safety issues and avoids holding connections open across requests.

  • Avoid singletons: Never register the context as a singleton or use a static instance, because DbContext is not thread-safe and can lead to stale data and connection pool exhaustion.

Connection Management and Async Operations

  • ADO.NET pooling: EF Core relies on ADO.NET connection pooling by default; the connection string should keep Pooling=true (the default) and set Max Pool Size only after monitoring actual concurrency.

  • Async operations: Use asynchronous methods such as ToListAsync and SaveChangesAsync to free threads during I/O, which is essential for scaling under concurrent load on Azure App Service.

Query Optimization and Bulk Operations

  • Pagination: Retrieve large datasets in chunks with Skip and Take rather than materializing entire tables.

  • Read-only queries: Apply AsNoTracking to queries that only read data; this disables change tracking and reduces memory pressure.

  • Eager loading: Use Include and ThenInclude or projections to fetch related data in a single round-trip and avoid the N+1 problem.

  • Set-based updates: For large updates or deletes, use ExecuteUpdateAsync and ExecuteDeleteAsync (EF Core 7+) to perform the work directly in the database without loading entities into memory.

Caching Strategies

  • Distributed cache: Store frequently accessed, static data in Azure Cache for Redis via IDistributedCache, or use IMemoryCache for single-instance scenarios.

  • Second-level cache: EF Core does not include a built-in second-level cache; community libraries such as EF Core Contrib can provide one, but evaluate whether the added complexity is warranted.

Azure-Specific Scaling Options

  • Compute tiers: Choose between the DTU-based and vCore-based purchasing models for Azure SQL Database; vCore offers more granular control over compute and storage.

  • Elastic pools: Use elastic pools with auto-scaling to absorb traffic spikes across multiple databases.

  • Sharding: Horizontal partitioning across multiple databases can scale beyond a single database limit, but requires custom routing logic and is generally a last resort.

Model Control Best for
DTU Bundled compute, storage, I/O Predictable, simple workloads
vCore Independent compute and storage scaling Variable or high-performance workloads

Monitoring and Diagnostics

  • Application Insights: Track query duration, dependency failures, and exception rates to identify bottlenecks.

  • EF Core logging: Enable detailed logging in development to spot inefficient queries, and inspect the underlying ADO.NET connection via Database.GetDbConnection() when needed.

63

Can Entity Framework be utilized to build applications that execute on non-Windows operating systems like Linux and macOS?

EF Core is fully cross-platform and can build applications that run on Linux and macOS, whereas legacy Entity Framework 6 was originally tied to the Windows-only .NET Framework. For any new project targeting non-Windows hosts, EF Core 8 on .NET 8 is the correct choice and behaves identically across operating systems.

Read full answer

Applies toEF Core 8 on .NET 8 (with notes on EF6.3+)

EF Core vs. Entity Framework 6

  • EF Core: Built on .NET Core / unified .NET (5+), modular, actively developed, and officially supported on Windows, Linux, and macOS.

  • EF6: Originally built for the .NET Framework and Windows-only. Starting with EF6.3, it can run on .NET Core 3.1 and .NET 5+, so it is no longer strictly Windows-only, but it is in maintenance mode, lacks newer EF Core features, and is not recommended for new cross-platform work.

Aspect EF Core 8 EF6.3+
Target runtime .NET 8 (cross-platform) .NET Framework or .NET Core/.NET 5+
Platform support Windows, Linux, macOS Windows; Linux/macOS possible on .NET Core but with limitations
Feature development Active Maintenance only
Bulk operations ExecuteUpdate / ExecuteDelete No equivalent
Recommended for new projects Yes No

Database Provider Compatibility

The ORM is platform-agnostic; the provider determines whether a database can be reached from a given OS. - SQL Server: Runs natively on Linux and in containers, so EF Core can connect from Linux/macOS hosts. - PostgreSQL, MySQL, SQLite: Providers such as Npgsql, Pomelo.EntityFrameworkCore.MySql, and Microsoft.EntityFrameworkCore.Sqlite are fully managed or bundle their native dependencies, and work on all major OSes. - Native dependencies: A few providers still require OS-level client libraries; consult the provider documentation rather than assuming libpq or similar must be installed manually.

Platform-Specific Considerations

Identical C# code can behave differently because of the host environment. - File paths: Linux/macOS use forward slashes; use Path.Combine or forward slashes in SQLite file paths. - Case sensitivity: Linux file systems are case-sensitive, so database file names and configuration keys must match exactly. - Permissions: The process identity needs read/write access to database files and folders on Unix-like systems. - Authentication: Windows Authentication / Integrated Security is unavailable on Linux; use SQL authentication, certificates, or Kerberos where the provider and server support it. - Environment configuration: Supply connection strings and secrets through environment variables or the .NET Secret Manager so the same artifact works on every OS.

Dependency Injection and Configuration

Register the DbContext with a scoped lifetime using AddDbContext; the same configuration works on every supported OS. Externalize connection strings and secrets so the same deployment artifact can run on Windows, Linux, or macOS. Avoid hardcoding credentials or Windows-only paths in data-access code.

Best Practices

  • Choose EF Core: It is the only fully supported, cross-platform Entity Framework product for new development.

  • Test on the target OS: Run migrations and integration tests in CI on Linux/macOS to catch path, permission, and provider issues early.

  • Avoid platform-specific APIs: Do not use the registry, Windows-specific security identifiers, or backslash paths in data-access logic.

  • Keep providers updated: Use the latest stable provider package for your database and target runtime.

64

What steps and considerations are required when building applications with Entity Framework Core on operating systems other than Windows?

Entity Framework Core is inherently cross-platform, enabling development on Linux and macOS unlike the Windows-restricted EF6, provided the chosen database provider supplies native binaries for the target operating system. Success depends on verifying provider compatibility and managing runtime-specific dependencies correctly.

Read full answer

Applies toEF Core 8 on .NET 8

Cross-Platform Architecture

EF Core runs on .NET 8, which supports Windows, Linux, and macOS. The ORM itself is pure managed code with no Windows-specific dependencies, so the same LINQ queries, change-tracking logic, and migrations work identically on every OS. The platform-specific surface area is almost entirely in the database provider, which often wraps native client libraries. When you deploy to a non-Windows server, confirm that the provider package ships native assets for the target Runtime Identifier (RID); otherwise the app will fail at runtime with a missing-library exception.

Database Provider Compatibility

Not every provider works everywhere. Microsoft.Data.SqlClient is the cross-platform replacement for the legacy Windows-only System.Data.SqlClient and can reach SQL Server from Linux and macOS, but the SQL Server instance itself must run on a supported platform (Linux containers or SQL Server on Linux). Microsoft.EntityFrameworkCore.Sqlite bundles native SQLite libraries for all major platforms and selects the correct one during restore, making it the safest choice for portable applications. Third-party providers such as Npgsql (PostgreSQL) and Pomelo.EntityFrameworkCore.MySql also ship multi-platform native assets. Avoid any provider that depends on Windows Authentication integrated security unless you have Kerberos correctly configured on the Linux host, and never use System.Data.SqlClient in a cross-platform project.

Native Dependencies and Publishing

When you publish a self-contained deployment, pass the target RID so the publish step bundles the right native binaries, e.g. dotnet publish -c Release -r linux-x64 --self-contained true. For framework-dependent deployments, the host machine must have the matching runtime and any native libraries the provider needs. In Docker, base images such as mcr.microsoft.com/dotnet/aspnet:8.0 guarantee that the OS layer matches the compiled dependencies; use the same RID in your Dockerfile that you used during publish. SQLite usually requires no extra steps because its package handles native asset selection automatically, but always test the published output on the actual target OS.

Best Practices and Pitfalls

  • Case Sensitivity: Linux file systems are case-sensitive, so migration file names, connection-string keys, and file paths must match exactly what the code references.

  • Path Separators: Use Path.Combine or forward slashes when building SQLite file paths; hardcoded backslashes break on Linux and macOS.

  • Authentication: Windows Authentication is unavailable on Linux; use SQL Server authentication, Azure Active Directory, or managed identities where the hosting platform supports them.

  • Collation: SQL Server instances on Linux can default to a different collation than Windows instances, which changes string comparison and indexing behavior—explicitly set the collation in the connection string or model if consistency matters.

  • CI/CD: Run integration tests on the same OS family as production to surface native dependency and path issues before release.

65

How do you configure and execute Entity Framework Core applications within macOS or Linux operating systems?

Entity Framework Core is cross-platform, so EF Core 8 applications can be configured and run on macOS and Linux using the .NET 8 SDK and a compatible database provider.

Read full answer

Applies toEF Core 8 on .NET 8

Version and Platform Compatibility

  • EF Core 8: supported on Windows, macOS, and Linux.

  • EF6: primarily targets .NET Framework; EF6.4+ can run on .NET Core/.NET 5+, but EF Core is the recommended choice for new cross-platform work.

  • Use EF Core for macOS/Linux projects to access current features such as ExecuteUpdate and ExecuteDelete.

Environment Setup

  • Install the .NET 8 SDK for your operating system.

  • Add Microsoft.EntityFrameworkCore plus a provider package such as Microsoft.EntityFrameworkCore.Sqlite or Npgsql.EntityFrameworkCore.PostgreSQL.

  • Install matching tooling with dotnet tool install --global dotnet-ef.

Dependency Injection

  • Register the DbContext with AddDbContext, which uses Scoped lifetime by default.

  • Do not share a DbContext across threads or register it as a Singleton.

  • Use AsNoTracking() for read-only queries.

Code Example

using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    public DbSet<Product> Products { get; set; }
}

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();
app.Run();

Platform-Specific Considerations

  • Linux file systems are case-sensitive. macOS defaults to case-insensitive APFS, although case-sensitive volumes are possible, so match casing in paths and migration files.

  • Use Path.Combine or forward slashes in file paths.

  • SQL Server is not natively available on macOS or Linux; use Docker, a remote instance, or another provider.

  • Run migrations from the terminal with dotnet ef migrations add InitialCreate and dotnet ef database update.

  • Execute the application with dotnet run.

Best Practices

  • Store connection strings and credentials in environment variables or the .NET Secret Manager, not in source code.

  • Use Include/ThenInclude or projections to avoid N+1 queries.

  • Use ExecuteUpdate and ExecuteDelete for bulk changes.

  • Verify provider compatibility and any OS-specific native dependencies.

66

What approaches can be used to manage database functions that vary by platform in a cross-platform EF application?

In a cross-platform EF application, platform-specific database functions can be handled by combining abstraction layers, conditional raw SQL, or EF Core’s built-in customization points like HasDbFunction and ISqlTranslationFactory.

Read full answer

Applies toEF Core 8 on .NET 8

Understanding the Challenge

EF Core’s LINQ provider translates expressions to SQL, but it does not know about every database function available in each provider. For example, SQL Server has FORMAT, SQLite has strftime, and PostgreSQL has to_char. Attempting to call these directly in LINQ throws a runtime exception because EF Core cannot find a matching translation. The goal is to write a single codebase that works across multiple providers without rewriting the data access layer for each.

Common Approaches

  • Abstraction with Dependency Injection: Define an interface for the platform-specific operation and implement it for each provider. Register the appropriate implementation based on the configured provider. This isolates provider-specific code and makes it easy to add new providers.

  • Conditional Raw SQL: Use FromSqlInterpolated or ExecuteSqlInterpolated with interpolated parameters to run provider-specific SQL, selecting the query string based on Database.ProviderName. This is quick but can become unmaintainable with many conditionals.

  • EF Core DbFunction Mapping: Use HasDbFunction in OnModelCreating to map a CLR method to a database function. You can conditionally set the function name per provider. This integrates with LINQ but is limited to database functions that exist on the server.

  • Custom SQL Translation (Advanced): Implement ISqlTranslationFactory to generate provider-specific SQL for a method call, giving full control over the translation. This is powerful but requires deep knowledge of EF Core internals.

Comparison of Approaches

Approach Maintainability Performance LINQ Integration When to Use
Abstraction with DI High Depends on implementation None (separate method) When the operation is complex or used in many places
Conditional Raw SQL Low High (direct SQL) No For one-off queries where performance matters
DbFunction Mapping Medium High Yes When the function is simple and used in LINQ
Custom SQL Translation High High Yes When you need seamless LINQ support across many providers

Best Practices and Pitfalls

  • Always use parameterized queries; never concatenate user input into raw SQL. Use FromSqlInterpolated or ExecuteSqlInterpolated to ensure parameters are bound.

  • Use AsNoTracking for read-only queries to improve performance.

  • Prefer abstraction over long switch statements to keep code maintainable.

  • Test your application on all target providers early and often.

  • Be aware that OnModelCreating runs once per context type; provider detection there is valid, but do not rely on runtime changes.

  • For bulk changes, use ExecuteUpdate/ExecuteDelete (EF Core 7+) instead of raw SQL when possible.

  • Register the DbContext as scoped (AddDbContext) and avoid resolving it from a singleton or transient service to prevent captive dependencies.

67

How can Entity Framework Core be combined with external libraries and application frameworks within a .NET solution?

Entity Framework Core integrates with third-party libraries and application frameworks primarily through the Dependency Injection (DI) container and its extensible provider model. This design lets EF Core participate in ASP.NET Core request pipelines, mapping libraries, caching layers, and logging frameworks without tight coupling, while keeping DbContext lifetime and configuration under the host’s control.

Read full answer

Applies toEF Core 8 on .NET 8

Dependency Injection and Lifetime

  • Registration: DbContext is added with AddDbContext<TContext> in IServiceCollection, which configures the context as Scoped by default.

  • Web frameworks: In ASP.NET Core, Blazor Server, and minimal APIs, a single context instance is created per request/scope and injected into controllers, endpoints, Razor components, or handlers.

  • Background work: For hosted services or job runners, use AddDbContextFactory<TContext> to create short-lived contexts inside an explicit scope, rather than capturing a scoped context in a singleton.

  • Provider configuration: Frameworks and libraries extend DbContextOptionsBuilder (for example, UseSqlServer, UseNpgsql) so the same registration pattern works across databases.

Provider Model and Extensibility

  • Database providers: Vendors ship EF Core providers as NuGet packages such as Npgsql.EntityFrameworkCore.PostgreSQL, Pomelo.EntityFrameworkCore.MySql, and Microsoft.EntityFrameworkCore.SqlServer. They translate LINQ expressions to dialect-specific SQL while preserving the DbContext API.

  • Interception: IInterceptor and DbCommandInterceptor let libraries inspect or modify commands, connections, and transactions without subclassing DbContext.

  • Design-time tooling: IDesignTimeDbContextFactory supplies a context instance for migrations and scaffolding when the runtime DI composition root is unavailable.

  • Mapping configuration: External libraries can organize entity configuration through IEntityTypeConfiguration<T> or add conventions via ModelBuilder.

Common Third-Party Integrations

Library / Framework Integration point Typical use
ASP.NET Core DI + middleware request-scoped DbContext, health checks with AddDbContextCheck
AutoMapper ProjectTo in LINQ translate DTO mapping into SQL projections
MediatR command/query handlers separate business logic from data-access code
Serilog Microsoft.EntityFrameworkCore logging categories structured query and command logging
Polly retry policies around unit-of-work calls handle transient database faults
Hangfire / Quartz.NET scoped job execution run background data operations safely

Best Practices and Pitfalls

  • Scoped lifetime only: Never register DbContext as Singleton; it is not thread-safe and can leak memory.

  • Avoid context capture: Do not store DbContext in a static field or pass it into a long-running background task without creating a scope.

  • Read-only queries: Use AsNoTracking when entities are not being updated to reduce change-tracking overhead.

  • Avoid N+1: Use Include/ThenInclude or projections (Select) to load related data in a single query.

  • Bulk changes: Prefer ExecuteUpdate and ExecuteDelete (EF Core 7+) over loading entities into memory when modifying many rows.

  • Raw SQL: If raw SQL is required, use FromSqlInterpolated or ExecuteSqlInterpolated with interpolated parameters; never concatenate user input into the SQL string.

  • Retry strategy: Prefer the built-in EnableRetryOnFailure for transient SQL errors unless a library-specific policy is required.

68

Which third-party tools can be used to manage Entity Framework Core migrations?

Several third-party tools can manage Entity Framework Core migrations, offering alternatives to the built-in dotnet ef workflow for teams that prefer SQL scripts, independent versioning, or GUI-based deployment.

Read full answer

Applies toEntity Framework Core 8 on .NET 8

Overview of Third-Party Migration Tools

  • FluentMigrator: A .NET library that defines migrations as C# classes with a fluent API. It supports SQL Server, PostgreSQL, MySQL, Oracle, SQLite, and others. Migrations are classes inheriting from Migration with Up() and Down() methods. It runs independently of EF Core and applies changes directly to the database, though it can coexist with an EF Core project.

  • DbUp: A database-agnostic tool that executes SQL scripts in a fixed order. It tracks applied scripts in a database table and does not require a DbContext. Users write scripts such as V1__Create_Users.sql; DbUp supports transactions, logging, and multiple providers.

  • Evolve: A lightweight open-source migration tool for .NET that reads SQL scripts from files or embedded resources. It supports SQL Server, PostgreSQL, MySQL, SQLite, and Oracle. It can be used alongside EF Core by creating an Evolve instance with a DbConnection and calling Migrate().

  • Migrator.NET: A legacy migration framework with a unified API across providers. It is less actively maintained and has limited EF Core relevance, so it is generally suitable only for existing projects.

  • Commercial tools: Products such as Redgate ReadyRoll provide GUI-based migration design, schema comparison, and deployment automation for EF Core workflows.

Comparison of Migration Tools

Tool Approach Database Support EF Core Relationship License / Status
FluentMigrator Code-first C# classes SQL Server, PostgreSQL, MySQL, Oracle, SQLite, etc. Independent; can coexist with EF Core MIT
DbUp Ordered SQL scripts SQL Server, PostgreSQL, MySQL, Oracle, SQLite, etc. No DbContext dependency; uses a connection string MIT
Evolve Ordered SQL scripts SQL Server, PostgreSQL, MySQL, SQLite, Oracle Standalone; invoked with a DbConnection MIT
Migrator.NET Code and scripts Multiple via providers Limited; legacy only Legacy, less maintained

Best Practices and Considerations

  • Choose one migration approach: Mixing EF Core migrations with a third-party tool in the same database can cause conflicting version history and deployment errors.

  • Versioning: Keep migration identifiers unique and monotonically increasing. FluentMigrator uses numeric attributes, while DbUp and Evolve rely on script filenames.

  • Transactions: Use transactions where the database provider supports them so a failed migration does not leave the schema partially updated.

  • Test rollbacks: Verify Down() methods or reverse scripts in a staging environment before relying on them in production.

  • Integrate into CI/CD: Run migrations through command-line runners or hosted services during deployment, using secure connection strings and no hardcoded credentials.

  • Match tool to team skills: SQL-centric teams often prefer DbUp or Evolve; teams comfortable with C# may prefer FluentMigrator.

69

Is it possible to build custom database providers for Entity Framework Core to support non-standard data sources?

Yes, Entity Framework Core supports building custom database providers to target data sources not officially supported, though this is a complex undertaking typically reserved for database engine vendors. For most application-level extensibility needs, such as logging or modifying SQL generation, interceptors are the recommended approach instead of rewriting the storage layer.

Read full answer

Applies toEF Core 8 on .NET 8

Architecture and Extensibility Points

EF Core separates the query pipeline from storage logic. The core engine handles LINQ translation and change tracking, while a provider supplies the storage services needed to connect, execute commands, manage transactions, map types, and generate SQL. This separation allows swapping storage backends without changing application code, provided the provider implements the required storage contracts.

Provider Versus Interceptor

Choosing the right extensibility mechanism prevents unnecessary complexity. A full provider handles storage and execution, whereas interceptors hook into existing pipelines.

Mechanism Scope Complexity Use Case
Custom Provider Storage engine Very High New database system
Interceptor Pipeline events Low Logging, auditing
Value Converter Data types Low Encryption, JSON

Implementation Steps

Creating a provider requires implementing several core services. 1. Create a DbContextOptionsExtension to hold configuration. 2. Implement the required storage services, such as connection, command, transaction, type mapping, and SQL generation services. 3. Register those services in the extension’s ApplyServices method. 4. Add the extension to the DbContextOptions. 5. Ensure transaction support and command execution align with EF Core expectations.

Best Practices

  • Scoped DbContext: Always register your DbContext as Scoped using AddDbContext; never share it across threads or keep it as a Singleton.

  • Read-Only Queries: Use AsNoTracking for read-only queries to reduce memory overhead and avoid change tracking.

  • Bulk Changes: Utilize ExecuteUpdate and ExecuteDelete (EF Core 7+) for bulk changes instead of loading entities into memory.

  • Security: Raw SQL must be parameterized using interpolated parameters; never concatenate user input into raw SQL strings.

71

Which object-relational mapping libraries besides Entity Framework are available for .NET, and under what circumstances should a developer prefer them over EF Core?

While Entity Framework Core is the standard ORM for .NET, alternatives such as Dapper and NHibernate serve specific scenarios where performance or fine-grained SQL control is paramount. Selecting the right tool depends on the complexity of the domain model and the performance requirements of the data access layer.

Read full answer

Applies to.NET 8 with EF Core 8 (Dapper, NHibernate, and LinqToDB discussed generally)

Popular Alternatives

  • Dapper: A micro-ORM that adds extension methods to IDbConnection. It maps query results to objects quickly with minimal overhead, ideal for high-throughput scenarios where abstraction is less valuable than speed.

  • NHibernate: A mature, full-featured ORM ported from Java’s Hibernate. Suitable for complex domain models and legacy databases with intricate mapping needs that exceed EF Core conventions.

  • LinqToDB: Focuses on LINQ translation performance and supports many database providers, often used when EF Core’s LINQ provider is too restrictive or generates inefficient SQL.

Selection Criteria

  • Performance: Use Dapper when raw query execution speed is critical, such as in high-frequency read operations or bulk inserts where EF Core change tracking adds significant overhead.

  • Control: Choose alternatives when you need precise control over SQL generation that EF Core’s LINQ provider cannot express efficiently without resorting to raw SQL strings.

  • Legacy: NHibernate may be preferable when migrating existing databases where its mapping flexibility handles awkward schemas better than EF Core conventions without extensive configuration.

Integrating with EF Core

It is common to use EF Core for domain logic and Dapper for specific high-performance queries within the same application. This balances productivity with performance effectively. Even in a hybrid setup, register the DbContext as Scoped via AddDbContext; never share a DbContext across threads or keep it as a Singleton. Use AsNoTracking for read-only queries in EF Core to avoid unnecessary tracking overhead before considering external tools. Inject IDbConnection separately for Dapper to maintain clear separation of concerns.

Common Pitfalls

  • Transaction Management: When mixing EF Core and Dapper, ensure both participate in the same transaction if consistency is required, often via a shared DbTransaction opened on the same connection.

  • Connection Handling: Manage connection lifetimes carefully; do not open long-lived connections when using micro-ORMs, as connection pooling and lifetime management remain the developer’s responsibility.

  • Testing Complexity: Mocking IDbConnection for unit tests can be more verbose than mocking DbContext, requiring careful design of repository interfaces.

  • Over-Engineering: Do not switch to a micro-ORM prematurely; EF Core 8 includes ExecuteUpdate and ExecuteDelete for bulk changes, reducing the need for alternatives in many cases.

72

What are the main advantages and improvements that EF Core offers over earlier Entity Framework versions such as EF6?

EF Core is a complete rewrite of Entity Framework that is cross-platform, lightweight, and modular, delivering better performance and a modern feature set compared to EF6.

Read full answer

Applies toEF Core 8 on .NET 8 (contrasted with EF6 on .NET Framework and .NET Core 3.1+)

Cross-Platform and Modular Architecture

  • EF Core runs on .NET 8 across Windows, Linux, and macOS; EF6 runs on .NET Framework and also on .NET Core 3.1+ via NuGet, but without the same level of cross-platform optimization.

  • It is delivered as focused NuGet packages (e.g., Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.SqlServer), so you reference only the provider and features you need instead of one monolithic assembly.

  • EF Core is code-first and DbContext-centric; the EDMX visual designer and ObjectContext stack from EF6 are not carried forward, which simplifies source control and CI/CD.

Performance and Querying Improvements

  • EF Core generates more efficient SQL and supports compiled queries, split queries (AsSplitQuery), and AsNoTracking for read-only workloads.

  • Global query filters let you apply tenant or soft-delete filters once instead of repeating them in every query.

  • Interceptors provide clean hooks for logging, auditing, and command modification without subclassing providers.

Modern Mapping and Modeling

  • Owned entities, value converters, and shadow properties make domain modeling more expressive without polluting entity classes.

  • Many-to-many relationships can be mapped without an explicit join entity (EF Core 5+).

  • JSON columns (EF Core 7+) and complex types plus primitive collections (EF Core 8) map rich object graphs directly to database columns.

Developer Productivity and Testing

  • The in-memory provider makes unit testing data access fast and isolated, without a real database.

  • Migrations are first-class and provider-aware, with better scaffolding and idempotent script generation.

  • ExecuteUpdate/ExecuteDelete (EF Core 7+) perform bulk changes in a single SQL statement instead of loading entities into memory.

Raw SQL and Extensibility

  • Raw SQL is supported through FromSqlInterpolated/ExecuteSqlInterpolated with interpolated, parameterized arguments, avoiding SQL injection; FromSqlRaw/ExecuteSqlRaw require explicit parameters.

  • Providers are pluggable, enabling PostgreSQL, MySQL, SQLite, Cosmos DB, and others beyond SQL Server.

Advantages and Limitations

Feature EF Core 8 EF6
Platform .NET 8 (Windows, Linux, macOS) .NET Framework; .NET Core 3.1+ via NuGet
Mapping style Code-first, DbContext Code-first, DbContext, plus EDMX designer
Global query filters Yes No
Split queries AsSplitQuery No
Bulk updates ExecuteUpdate/ExecuteDelete (EF Core 7+) No (must load entities)
JSON column mapping EF Core 7+ No
Primitive collections EF Core 8 No
Interceptors Yes Limited (IDbCommandInterceptor)
  • EF Core is the actively developed, recommended stack; EF6 is in maintenance mode and only receives critical fixes.

  • Some EF6 features (visual designer, ObjectContext, certain EDMX workflows) are not ported, so legacy apps may need migration effort.

  • Behavior can differ by provider and database engine, so test against your actual target database rather than only the in-memory provider.

73

How do Entity Framework Core versions align with .NET releases in terms of compatibility and long-term support?

Entity Framework Core releases are versioned and supported in step with the .NET platform: each EF Core major version is built for a specific .NET runtime, shares its support lifecycle, and should be paired with compatible database providers. This version alignment helps ensure that EF Core can use current runtime features while following the same support and compatibility guarantees as the underlying framework.

Read full answer

Applies toEF Core 8 on .NET 8

Version Alignment Strategy

  • Target Frameworks: EF Core 8 is built for .NET 8. Earlier EF Core versions target the .NET version they shipped with, and Microsoft documents which .NET runtimes each EF Core release supports.

  • Package Dependencies: The Microsoft.EntityFrameworkCore package version should be chosen to match the intended EF Core release and target framework, avoiding runtime mismatches or missing API errors.

  • Provider Compatibility: Database providers such as Npgsql or Pomelo.EntityFrameworkCore.MySql must be updated to versions explicitly compatible with the selected EF Core release.

Support Lifecycle

  • Long-Term Support (LTS): .NET 8 is an LTS release, supported for three years. EF Core 8 inherits this lifecycle and receives bug fixes and security patches throughout that period.

  • Standard Term Support (STS): Non-LTS .NET versions receive support for 18 months. Using an STS stack requires more frequent upgrades to remain within supported boundaries.

  • End of Support: When the .NET version reaches end-of-life, the corresponding EF Core version no longer receives updates, so migrating to a newer supported stack is necessary for continued security compliance.

Breaking Changes and Migration

  • Major Version Upgrades: Moving between major EF Core versions may introduce breaking changes in APIs, query behavior, or configuration patterns.

  • Migration Guides: Microsoft publishes breaking change documentation for each release, outlining required code modifications and deprecated features.

  • Database Migrations: EF Core model changes require generating new migrations with dotnet ef migrations add before deploying schema changes to production databases.

Best Practices for Upgrading

  • Test Thoroughly: Run integration tests against the target database to catch behavioral differences in query generation, change tracking, or lazy loading.

  • Update Providers First: Ensure third-party database providers are updated to versions compatible with the new EF Core release before upgrading the ORM itself.

  • Review Deprecations: Check compiler warnings for deprecated APIs and replace them with their recommended alternatives.

  • Dependency Injection: Register the DbContext as Scoped using AddDbContext in production applications to ensure proper lifetime management across requests.

74

Which capabilities are planned for future releases of Entity Framework Core?

Upcoming Entity Framework Core releases are expected to extend LINQ translation, mapping flexibility, ahead-of-time (AOT) compatibility, and provider/tooling quality rather than replace the core DbContext programming model. Because roadmap items are discussed in previews and GitHub epics, treat any specific API as provisional until it ships in a stable release.

Read full answer

Applies toEF Core 8 (current stable); roadmap directions described below refer to preview/announced work for EF Core 9 and later.

Roadmap Themes

  • Broader LINQ-to-SQL translation, reducing the need for client-side evaluation and AsEnumerable workarounds.

  • More flexible mapping for complex types, JSON columns, and primitive collections, closing gaps left by EF Core 8.

  • Reduced runtime reflection and better trimming/Native AOT support, which matters for cloud-native and serverless deployments.

  • Steady provider improvements for SQL Server, PostgreSQL, SQLite, and Cosmos DB, with design-time tooling following the same cadence.

Querying and LINQ

  • Translation coverage is expected to grow incrementally for grouping, window functions, set operations, and collection projections.

  • Global query filters remain the stable mechanism in EF Core 8; preview discussions have explored finer-grained enable/disable control, but no named-filter API has shipped.

  • Performance work targets SQL generation, parameter handling, and allocations in hot query paths, which can improve throughput without changing application code.

Mapping and Types

  • Complex types should become more flexible, with better support for optional properties, nested structures, and constructor-initialized values.

  • JSON column mapping is expected to mature, making it easier to store and query semi-structured data without separate tables or manual serialization.

  • Primitive collections are likely to gain broader provider support and more consistent querying behavior across databases.

Performance and Deployment

  • AOT-friendly code paths and source-generated model components reduce startup time and memory, which is important for serverless and containerized workloads.

  • Migrations and model building may see further startup-time and memory improvements, especially for large models.

  • Bulk operations such as ExecuteUpdate and ExecuteDelete (EF Core 7+) are expected to gain broader translation support, reducing the need for raw SQL.

Provider and Tooling Considerations

  • Database providers often introduce provider-specific features before they appear in the core abstractions, so check provider release notes.

  • Design-time tooling for migrations, scaffolding, and dotnet ef commands generally follows the core release cadence.

  • Preview packages can contain breaking changes; pin versions and validate migrations before upgrading production applications.

Best Practices for Tracking Upcoming Changes

  • Follow the official EF Core GitHub roadmap and release notes rather than relying on preview blog posts alone.

  • Test preview packages in isolated branches using representative queries, migrations, and production-like data volumes.

  • Register DbContext as scoped with AddDbContext; never share a context across threads or keep it as a singleton.

  • Use AsNoTracking for read-only queries and prefer projections or Include/ThenInclude to avoid N+1 query problems.

  • Re-run performance and migration tests after each upgrade, because translation behavior and default mappings can change between versions.

75

In what ways does user input shape the planning and feature evolution of Entity Framework?

Community feedback directly shapes the Entity Framework Core roadmap through transparent open-source channels managed by the Microsoft data team. The team aggregates bug reports, feature requests, and usage data to determine which improvements are implemented in upcoming releases.

Read full answer

Applies toEF Core 8 on .NET 8

Feedback Collection Mechanisms

  • GitHub Issues: The primary channel is the official repository dotnet/efcore, where users file issues and discuss proposals.

  • Surveys and Telemetry: Annual .NET surveys and usage data highlight common performance bottlenecks or configuration errors.

  • Design Notes: Meeting notes are published regularly, allowing developers to see how specific suggestions are debated internally. Community standups and live streams also provide real-time interaction between users and engineers.

Roadmap Transparency

Planned features are often documented in GitHub projects or announced via the .NET blog before implementation begins. This allows stakeholders to verify whether their requested capabilities are scheduled for a specific version. If a high-demand feature is deferred, the team typically explains the technical constraints or architectural risks involved. Milestones are updated to reflect current progress toward the next major release, which usually aligns with the annual .NET release cycle.

Feature Prioritization

Not all requests are implemented immediately; the team balances community demand against engineering cost and strategic goals. Issues with higher reaction counts on GitHub often receive earlier attention, especially if they affect broad usage scenarios. Critical bugs reported by production users typically supersede new feature work to maintain stability. Compatibility with existing applications is also a major factor in deciding when breaking changes are introduced, ensuring migrations remain manageable.

Open Source Contributions

Developers are encouraged to submit pull requests for bug fixes or minor enhancements directly to the source code. Accepted contributions undergo rigorous code review to ensure they align with the existing architecture and testing standards. This collaborative model reduces the burden on the core team while fostering a sense of ownership among users. Contributors must sign a license agreement before their code can be merged, and new features typically require accompanying unit tests.

Guidelines for Effective Feedback

  • Search First: Users should search for existing issues before creating new ones to avoid duplication.

  • Repro Projects: Providing a minimal reproducible project significantly increases the likelihood of a bug being understood and fixed quickly.

  • Clear Communication: Clear titles and detailed environment specifications help triage teams categorize issues correctly, while respectful communication ensures productive discussions. Constructive comments on design proposals help refine APIs before they become stable and publicly released.

76

What approaches does Entity Framework provide for creating and initializing a database schema during development and deployment?

In Entity Framework Core, database schema creation and initialization are managed primarily through Migrations, which version-control schema changes and apply them across environments; EnsureCreated and EnsureDeleted offer a simpler but non-incremental alternative for development, while the initializer patterns from EF6 are not available.

Read full answer

Applies toEF Core 8 on .NET 8

Migrations as the Primary Strategy

EF Core uses a code-first migration workflow. A migration is a C# snapshot of the model diff, generated with dotnet ef migrations add <Name> (optionally specifying --context, --project, and --startup-project). The migration contains Up and Down methods that create or revert tables, columns, indexes, and constraints. Applying migrations with dotnet ef database update or context.Database.Migrate() records each applied migration in the __EFMigrationsHistory table, so later deployments apply only pending changes. Because migrations are tracked and can express both additive and destructive changes, they support incremental evolution from development through production.

EnsureCreated and EnsureDeleted

For prototypes, integration tests, or ephemeral databases, context.Database.EnsureCreated() creates the database and all tables immediately if they do not already exist, and EnsureDeleted() drops the database. These methods bypass migrations entirely: they do not create __EFMigrationsHistory, cannot apply incremental changes, and cannot detect model drift. If the model changes after EnsureCreated has run, the database must be deleted or the project must switch to migrations manually. They are therefore unsuitable for any environment where data or schema history must be preserved.

Applying Migrations at Runtime

In ASP.NET Core and generic hosts, migrations can be applied during startup by resolving the DbContext inside a temporary service scope. Because DbContext is registered as a scoped service, resolving it from the root provider can trigger lifetime validation errors or cause singleton-like disposal problems; IServiceScopeFactory or host.Services.CreateScope() creates a proper scope that is disposed after Migrate() completes. In scaled-out deployments, only one instance should run migrations at a time, or the deployment process should apply migrations before the application instances start, to avoid concurrent schema modification.

Deployment and Scripting

For production deployments, teams often generate a SQL script rather than letting the application run migrations live. dotnet ef migrations script produces a script covering a range of migrations; the --idempotent flag makes it safe to run repeatedly by checking __EFMigrationsHistory before applying each migration. The script can be executed by a CI/CD pipeline with a privileged deployment account, while the application runtime account needs only read/write and execute permissions. This separates schema change execution from application startup and avoids holding long-lived migration locks.

Comparison of Initialization Approaches

Approach Tracks history Incremental Safe for production Typical use
Migrations Yes, in __EFMigrationsHistory Yes Yes Development, testing, production
EnsureCreated / EnsureDeleted No No No Prototypes, ephemeral test databases
EF6 Database.SetInitializer N/A N/A N/A Legacy EF6 only; not available in EF Core

Best Practices and Pitfalls

  • Never use EnsureCreated in production: it cannot update an existing schema and silently ignores migration history.

  • Keep DbContext scoped: always resolve it inside a using scope when calling Migrate() at startup.

  • Do not mix EF6 initializers: Database.SetInitializer does not exist in EF Core; use migrations instead.

  • Seed data through migrations or HasData: avoid initialization strategies for default data, and remember that HasData seeds only on migration creation unless explicitly updated.

  • Review generated migrations: before applying, inspect the diff for unintended drops or data-loss operations.

  • Use parameterized raw SQL only when necessary: if a migration calls ExecuteSqlRaw, pass parameters explicitly and never concatenate user input.

77

Can Entity Framework Core be used with a database that has no official provider by implementing a custom database provider?

Yes. EF Core can target a database without an official provider by implementing a custom database provider, but this is a substantial undertaking because you must supply connection handling, SQL generation, type mapping, and migration support yourself. For most applications, using an existing community provider or a supported database is the more practical choice.

Read full answer

Applies toEF Core 8 on .NET 8

How It Works

EF Core separates its core ORM services from database-specific behavior through a provider model. A provider plugs into DbContext configuration and determines how LINQ queries are translated into SQL, how database connections and commands are created, how CLR values map to database types, and how schema changes are generated for migrations. Registration is normally done in AddDbContext through a provider-specific Use... extension method.

What a Custom Provider Must Cover

  • Connection and command creation: opening connections, creating commands, and reading results through the underlying ADO.NET driver.

  • Query translation: converting LINQ expressions into SQL that the target database understands.

  • Type mappings: mapping CLR types such as int, string, DateTime, Guid, and decimal to the database’s native types.

  • CRUD SQL generation: producing correct INSERT, UPDATE, DELETE, and SELECT statements, including batching where supported.

  • Migrations: generating DDL for creating and altering tables, columns, indexes, constraints, and other schema objects.

  • Transactions and concurrency: handling transaction scopes, isolation levels, and concurrency tokens correctly.

Practical Considerations

  • Building and maintaining a provider is a large, long-term effort, especially as EF Core adds new features and query capabilities.

  • You must test thoroughly against the actual target database, including edge cases in SQL syntax, data types, null handling, and transactions.

  • Feature coverage may be incomplete at first; advanced scenarios such as JSON columns, spatial data, or complex LINQ translations may require additional work.

  • Community providers may already exist for less common databases, so check before building your own.

When an Alternative Is Better

  • If the database has an existing EF Core provider, prefer that provider over a custom implementation.

  • If no provider exists and the database is niche or legacy, using ADO.NET directly, Dapper, or another lightweight data access library may be simpler and lower risk.

  • EF Core does not include a built-in generic ODBC provider, so ODBC alone is not a shortcut to full EF Core support.

Best Practices

  • Keep the provider version aligned with the EF Core version it targets; provider internals can change between EF Core releases.

  • Implement only the features your application needs first, then expand coverage deliberately.

  • Use parameterized SQL for all raw commands and never concatenate user input into SQL.

  • Validate and encode input at application boundaries, and avoid storing secrets or connection strings in source code.

  • Prefer projections and AsNoTracking for read-only queries, and use ExecuteUpdate or ExecuteDelete for bulk changes when supported by your provider.

78

How does Entity Framework Core handle mapping database views and executing stored procedures within a DbContext?

Entity Framework Core treats database views as entities mapped to specific database objects, while stored procedures are invoked via raw SQL interpolation or mapped directly to insert/update/delete operations in EF Core 7 and later. This allows developers to leverage existing database logic without abandoning the ORM pattern.

Read full answer

Applies toEF Core 8 on .NET 8

Mapping Database Views

To map a view, define a DbSet<T> in your DbContext and configure the entity to point to the view name using ToView. Since views often lack primary keys or are intended for reading, you may use HasNoKey() for query types, though regular entities work if keys exist. EF Core does not automatically detect whether the underlying object is a view or table; it simply sends SQL against the configured name.

Executing Stored Procedures

Stored procedures can be executed using FromSqlInterpolated for queries returning entities, or ExecuteSqlInterpolated for non-query commands. EF Core 7+ allows mapping stored procedures directly to entity lifecycle events (insert, update, delete) using Fluent API configuration, enabling automatic invocation during SaveChanges. For ad-hoc execution, raw SQL methods remain the standard approach.

Code Example

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;

public class ProductSummary
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal TotalSales { get; set; }
}

public class AppDbContext : DbContext
{
    public DbSet<ProductSummary> ProductSummaries { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ProductSummary>()
            .ToView("ProductSummaryView");

        modelBuilder.Entity<ProductSummary>()
            .InsertUsingStoredProcedure("InsertProductSummary", sp =>
                sp.HasParameter(p => p.Name));
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=.;Database=Db;Trusted_Connection=True;");
    }
}

// Usage
using var context = new AppDbContext();
var summaries = context.ProductSummaries
    .FromSqlInterpolated($"EXEC GetSummaries @Year = {2024}")
    .AsNoTracking()
    .ToList();

Best Practices and Pitfalls

  • Parameterization: Always use FromSqlInterpolated or ExecuteSqlInterpolated to prevent SQL injection; never concatenate user input into FromSqlRaw.

  • DbContext Lifetime: Register the DbContext as Scoped in dependency injection containers using AddDbContext; never share it across threads or keep it as a Singleton.

  • View Updates: Avoid attempting to save changes to view-mapped entities unless the view is explicitly updatable and configured with stored procedure mappings.

  • Bulk Operations: Use ExecuteUpdate or ExecuteDelete for bulk changes instead of retrieving entities, unless the logic resides strictly within a stored procedure.

  • Read-Only Queries: Apply AsNoTracking() when querying views to improve performance and avoid concurrency conflicts, as EF cannot reliably track changes against a view.

79

How does Entity Framework handle SQL Server-specific data types such as hierarchyID and geography?

EF Core 8 handles SQL Server spatial types through NetTopologySuite, while hierarchyid has no first-class mapping and is usually treated as a string or queried with raw SQL.

Read full answer

Applies toEF Core 8 on .NET 8 with `Microsoft.EntityFrameworkCore.SqlServer` (spatial support requires `Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite`).

Spatial Types (geography / geometry)

  • Add the Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite NuGet package and enable it when configuring the provider with UseNetTopologySuite().

  • Entity properties use NetTopologySuite types such as Point, Polygon, LineString, and Geometry, which map to geography/geometry columns.

  • EF Core translates LINQ spatial operations such as distance, intersection, and containment into SQL Server spatial methods like STDistance, STIntersects, and STContains.

  • Spatial columns are created with the correct SQL Server type when using migrations, and SRID handling is preserved.

hierarchyid

  • EF Core 8 has no built-in CLR mapping for hierarchyid; it is not supported as a first-class type by the SQL Server provider.

  • Common approaches include storing the value as a string property, applying a value converter, or querying and manipulating hierarchy nodes through parameterized raw SQL using FromSqlInterpolated or ExecuteSqlInterpolated.

  • SQL Server functions such as GetAncestor, GetDescendant, IsDescendantOf, and GetLevel are typically used for hierarchy traversal.

  • Because hierarchyid comparisons and tree traversal are best expressed in T-SQL, many teams keep hierarchy logic in stored procedures or raw SQL rather than LINQ.

Querying and Performance

  • Use AsNoTracking() for read-only spatial queries to avoid change-tracking overhead.

  • Project only the columns you need to avoid pulling large spatial payloads.

  • For bulk updates to spatial or hierarchy data, prefer ExecuteUpdate/ExecuteDelete (EF Core 7+) over loading entities.

Best Practices and Pitfalls

  • Always register the DbContext as Scoped (AddDbContext); never share it across threads or keep it as a Singleton.

  • Never concatenate user input into FromSqlRaw; use FromSqlInterpolated/ExecuteSqlInterpolated with parameters.

  • Ensure the database column SRID matches the CLR geometry’s SRID to avoid silent empty results.

  • For hierarchyid, validate and sanitize any path strings before binding them as parameters.

  • If you need full hierarchy traversal in LINQ, consider materializing the relevant subtree with raw SQL first, then mapping to entities.

80

How does Entity Framework Core support NoSQL databases, and what are its capabilities and limitations?

Entity Framework Core supports NoSQL databases through pluggable database providers, most notably the official Azure Cosmos DB provider and the MongoDB provider, allowing you to use the familiar DbContext, LINQ, and change-tracking APIs against document stores.

Read full answer

Applies toEF Core 8 on .NET 8 (Cosmos provider `Microsoft.EntityFrameworkCore.Cosmos` 8.x; MongoDB provider `MongoDB.EntityFrameworkCore` 8.x)

How It Works

EF Core is built around a provider model: the core ORM (query translation, change tracking, migrations) is database-agnostic, and a provider adapts it to a specific store. For NoSQL, the provider translates LINQ queries into the store’s native query language (Cosmos SQL or MongoDB query documents) and maps JSON documents to .NET objects. You register the provider in OnConfiguring or via AddDbContext, exactly as with a relational provider, and work with DbSet<T> entities as usual.

Supported Providers

  • Azure Cosmos DB (Microsoft.EntityFrameworkCore.Cosmos): the official, first-party NoSQL provider; supports the SQL API (Core API) with document containers.

  • MongoDB (MongoDB.EntityFrameworkCore): an EF Core provider for MongoDB, letting you use LINQ and change tracking over MongoDB collections.

  • Other community providers exist (for example, for RavenDB), but Cosmos DB and MongoDB are the mainstream, well-supported options.

Capabilities

  • LINQ queries translated to the store’s query language, including filtering, projection, and ordering.

  • Change tracking and SaveChanges for inserts, updates, and deletes on tracked entities.

  • Include/ThenInclude for owned types and embedded documents (Cosmos models documents as nested objects).

  • Optimistic concurrency via ETags (Cosmos) or version fields.

  • Migrations support for Cosmos (creating containers and indexes), though schema is flexible by nature.

Limitations

  • No cross-container joins or transactions spanning multiple containers in Cosmos DB; queries are scoped to a single container/partition.

  • Relational features such as foreign keys, complex many-to-many joins, and SQL-style aggregations are not available or are translated differently.

  • Performance characteristics differ: document stores favor denormalized, aggregate-root models, so deep Include chains can be expensive or unsupported.

  • Not every LINQ operator translates; unsupported operators fall back to client evaluation or throw, depending on configuration.

  • Cosmos DB requires a partition key per container; queries that omit it may trigger cross-partition scans and higher charges.

Best Practices

  • Model data as aggregate roots with embedded owned types rather than normalized tables.

  • Use AsNoTracking for read-only queries to reduce overhead.

  • Prefer projections (Select) to fetch only the fields you need.

  • Partition your Cosmos containers by a high-cardinality key and include the partition key in queries to avoid cross-partition scans.

  • Use ExecuteUpdate/ExecuteDelete (EF Core 7+) for bulk changes where the provider supports them.

  • Store connection strings, account keys, and credentials in secure configuration (e.g., Azure Key Vault, user secrets, environment variables) rather than hardcoding them.

Sign in to keep reading

You've opened 2 full answers. Sign in to read all 80 Entity Framework answers, mark what you know and pick up where you left off.

All Web and Mobile Development subjects