Q1. What is ORM and what is Entity Framework?
ORM (Object-Relational Mapping) is a programming technique that maps database tables to object-oriented classes, allowing developers to work with databases using objects instead of raw SQL queries. Entity Framework is Microsoft's ORM framework for .NET that provides an abstraction layer between application code and the database, enabling LINQ queries, automatic change tracking, and code-first or database-first development approaches. Entity Framework translates LINQ queries into SQL at runtime, executes them against the database, and materializes results into strongly-typed objects. Using an ORM like Entity Framework improves productivity, reduces repetitive data access code, and enables database-agnostic development. Entity Framework Core is the modern, lightweight, and cross-platform implementation recommended for new projects.
// Using Entity Framework to query users
// Instead of writing raw SQL:
// SELECT * FROM Users WHERE Age > 18
// You can write LINQ with Entity Framework:
using (var context = new ApplicationDbContext())
{
var adults = context.Users
.Where(u => u.Age > 18)
.ToList();
// EF translates this to SQL automatically
}
Q2. What are the differences between Entity Framework 6 and Entity Framework Core?
Entity Framework 6 (EF6) is the legacy ORM for the .NET Framework, while Entity Framework Core (EF Core) is the modern, lightweight, open-source ORM for .NET Core and .NET 5+. EF Core is a complete rewrite of EF6 with improved performance, a smaller codebase, and support for cross-platform development. EF Core is async-first, has better LINQ support, lazy loading, and provides better performance for many scenarios. EF6 had some features that EF Core initially lacked (like spatial data, complex types), but EF Core has gradually added these capabilities. EF Core is the recommended choice for new projects, while EF6 is maintained for backward compatibility with legacy .NET Framework applications. Performance-wise, EF Core generally outperforms EF6, especially for batch operations and async workloads.
Q3. What are Database-First, Code-First, and Model-First approaches?
Database-First approach starts with an existing database, and Entity Framework generates model classes and DbContext from the database schema using the scaffolding tool. Code-First approach starts with writing model classes, and Entity Framework creates the database schema from the models using migrations. Model-First approach (EF6 only) allows designing entities visually in a designer, and EF generates both models and database schema. Code-First is the modern, recommended approach as it keeps the application code as the source of truth and integrates well with version control. Database-First is useful when integrating with legacy databases. Model-First is rarely used in modern development. Each approach has different workflows; Code-First promotes test-driven development and clean code, while Database-First is useful for teams with dedicated database administrators.
// Code-First: Define model classes first
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public DateTime CreatedDate { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public int UserId { get; set; }
public User User { get; set; }
}
// Define DbContext
public class ApplicationDbContext : DbContext
{
public DbSet Users { get; set; }
public DbSet Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connection_string");
}
}
// Then use migrations to create database
// dotnet ef migrations add InitialCreate
// dotnet ef database update
Q4. What is DbContext?
DbContext is the main class in Entity Framework that represents a session with the database, acting as a bridge between domain classes and the database. It tracks changes to entities, manages the identity map (caching), and coordinates the persistence of changes back to the database. DbContext exposes DbSet<TEntity> properties representing collections of entities that map to database tables. Every Entity Framework application must define a DbContext class by inheriting from DbContext and configuring database connections, entity mappings, and relationships in OnConfiguring or appsettings.json. DbContext should be disposed properly to release database connections; it should typically be scoped per request in web applications. Understanding DbContext is fundamental to using Entity Framework effectively.
Q5. What is DbSet?
DbSet<TEntity> is a generic collection class in Entity Framework that represents a table in the database, exposing methods for querying and modifying entities. DbSet allows you to write LINQ queries against database tables, add new entities, update existing ones, and delete entities. DbSet inherits from IQueryable, enabling LINQ-to-Entities queries that are translated to SQL. Each DbSet property in DbContext maps to a database table, and the property name typically determines the table name (though this can be customized). DbSet operations don't immediately persist changes; changes are tracked in memory until SaveChanges() is called. DbSet is essential for performing any data access operations in Entity Framework, making it the primary interface for working with entities.
Q6. What is OnModelCreating and how does Fluent API work?
OnModelCreating is a method in DbContext that allows configuring the Entity Data Model (EDM) using the Fluent API, which is a programmatic approach to entity mapping. Fluent API provides a chainable, lambda-based interface for configuring entity properties, relationships, constraints, and database-specific settings without using Data Annotations. Fluent API is more powerful than Data Annotations, supporting complex scenarios like composite keys, shadow properties, value converters, and table splitting. Configuration in OnModelCreating takes precedence over Data Annotations when both are present. Fluent API promotes separation of concerns by keeping model classes clean and configuration centralized. Using OnModelCreating with Fluent API is the recommended approach for complex mappings and database-specific configurations in EF Core.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configure User entity
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(100);
entity.Property(e => e.Email)
.IsRequired()
.HasMaxLength(256);
entity.HasMany(e => e.Products)
.WithOne(p => p.User)
.HasForeignKey(p => p.UserId)
.OnDelete(DeleteBehavior.Cascade);
entity.ToTable("Users");
});
// Configure Product entity
modelBuilder.Entity(entity =>
{
entity.HasKey(e => e.Id);
entity.Property(e => e.Title)
.IsRequired()
.HasMaxLength(200);
entity.Property(e => e.Price)
.HasPrecision(18, 2);
});
}
Q7. What is the difference between Fluent API and Data Annotations?
Data Annotations are attributes applied directly to model properties to configure entity mappings (e.g., [Required], [MaxLength], [Index]), keeping configuration close to the property definition. Fluent API is a method-based approach in OnModelCreating that configures mappings through method calls, providing more control and supporting complex scenarios that Data Annotations cannot express. Fluent API is more powerful, supporting composite keys, shadow properties, value converters, and database-specific settings, while Data Annotations are simpler but limited in scope. Fluent API keeps models clean and free from database-specific attributes, promoting separation of concerns. In practice, both approaches can be used together; Data Annotations for common configurations and Fluent API for complex mappings. Fluent API is generally preferred in enterprise applications due to its flexibility and power.
Q8. How are connection strings configured in Entity Framework?
Connection strings in Entity Framework can be configured in several ways: in OnConfiguring method using UseSqlServer/UseMySql/UsePostgreSql, in appsettings.json (recommended for ASP.NET Core), or passed to DbContext constructor. The appsettings.json approach is the modern, recommended way where connection strings are stored in a configuration file and injected into DbContext through dependency injection. In OnConfiguring, you can configure connection strings programmatically, though this makes it harder to manage different connection strings for different environments. For ASP.NET Core, DbContext is registered in dependency injection with connection string from configuration, making the same code work across development, staging, and production environments. Proper connection string management ensures security (keeping credentials out of source control) and flexibility (easy environment switching).
Q9. What are Entity Framework Migrations?
Migrations are a versioned history of database schema changes in Entity Framework, allowing you to evolve the database schema as your models change over time. When you modify models in Code-First development, you create a migration using "Add-Migration" that generates C# code representing the changes (create tables, add columns, etc.). Migrations can be applied to the database using "Update-Database" and rolled back using "Remove-Migration". Each migration is reversible and traceable, maintaining a complete history of schema changes. Migrations support multiple database providers simultaneously, allowing the same model to work with SQL Server, PostgreSQL, or other databases. Migrations enable team collaboration by version-controlling schema changes and applying them consistently across environments. Understanding migrations is essential for managing database evolution in Entity Framework applications.
Q10. How do you add, apply, and remove migrations?
To create a migration after modifying models, use the "Add-Migration MigrationName" command in Package Manager Console or "dotnet ef migrations add MigrationName" in the CLI. This generates a migration class with Up and Down methods in the Migrations folder. To apply migrations and update the database schema, use "Update-Database" command which executes all pending migrations against the database. To remove the latest migration, use "Remove-Migration" which deletes the migration file and reverts the model snapshot. Multiple migrations can be created before applying them, then applied together in sequence. Migrations maintain a migration history table in the database tracking which migrations have been applied. Understanding migration commands is essential for managing database schema changes effectively in a team environment.
Q11. How do you seed data in Entity Framework?
Seeding data in Entity Framework involves populating the database with initial data required by the application. This can be done in the OnModelCreating method using HasData() method, in migrations using Sql() method for raw SQL, or through a separate seeding service after migrations. HasData() is recommended for small, static reference data like status codes or categories, while migrations with Sql() are useful for larger datasets or data transformation during schema changes. Seeding ensures the database has necessary data for the application to function correctly, particularly important for enumerations, default settings, and test data. Seed data should be idempotent (safe to run multiple times) and version-controlled alongside migrations. Proper seeding strategy ensures consistency across all environments (development, staging, production).
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Seed reference data using HasData
modelBuilder.Entity().HasData(
new Status { Id = 1, Name = "Active" },
new Status { Id = 2, Name = "Inactive" },
new Status { Id = 3, Name = "Pending" }
);
// Seed users
modelBuilder.Entity().HasData(
new User
{
Id = 1,
Name = "John Doe",
Email = "john@example.com",
CreatedDate = DateTime.Now
},
new User
{
Id = 2,
Name = "Jane Smith",
Email = "jane@example.com",
CreatedDate = DateTime.Now
}
);
}
Q12. What is the migration history table?
The migration history table is a system table (__EFMigrationsHistory by default) that Entity Framework creates to track which migrations have been applied to the database. Each row in the history table represents an applied migration with its name, product version, and application timestamp. This table prevents re-running the same migration multiple times and enables Entity Framework to determine which pending migrations need to be applied. The migration history table is created automatically when the first migration is applied and should not be manually edited. Understanding the migration history table helps troubleshoot migration issues and ensures consistent database state across environments. The table name can be customized in OnConfiguring if needed for specific requirements.
Q13. What are LINQ queries with Entity Framework?
LINQ (Language Integrated Query) with Entity Framework allows you to write strongly-typed queries against the database using C# syntax, which are translated to SQL at runtime. LINQ supports standard query operators like Where, Select, OrderBy, GroupBy, Join, Any, All, FirstOrDefault, and many others. LINQ queries are lazily evaluated (deferred execution) when the query is defined, but actually executed against the database when you call ToList(), FirstOrDefault(), or iterate over results. LINQ-to-Entities queries are type-safe, intellisense-enabled, and provide compile-time checking, making them safer than raw SQL queries. Understanding LINQ is essential for effective Entity Framework usage and enables writing clean, maintainable data access code. LINQ supports both method syntax and query syntax, with method syntax being more commonly used in modern applications.
using (var context = new ApplicationDbContext())
{
// Method syntax - commonly used
var activeUsers = context.Users
.Where(u => u.IsActive)
.OrderBy(u => u.Name)
.ToList();
// Query syntax
var premiumProducts = (from p in context.Products
where p.Price > 100
orderby p.Title
select p).ToList();
// Complex query with multiple operations
var result = context.Users
.Include(u => u.Products)
.Where(u => u.CreatedDate > DateTime.Now.AddYears(-1))
.Select(u => new
{
u.Name,
ProductCount = u.Products.Count()
})
.ToList();
}
Q14. What is deferred execution in LINQ?
Deferred execution means that LINQ queries are not executed immediately when defined; instead, they are executed when you materialize the results by calling methods like ToList(), FirstOrDefault(), Count(), or iterating with foreach. This allows you to compose and refine queries before sending them to the database, enabling more efficient query generation. For example, you can build a query conditionally, adding filters based on runtime conditions before execution. Deferred execution is powerful but can be confusing; if you iterate over results multiple times, the query executes multiple times against the database. Understanding deferred execution is important for writing efficient Entity Framework code and avoiding unexpected database hits. To execute immediately, use methods like ToList() or ToArray() to materialize results into memory.
Q15. What is the difference between ToList(), FirstOrDefault(), and Any()?
ToList() executes the query immediately and returns all matching results as a List<T> in memory, suitable for processing multiple results. FirstOrDefault() executes the query but returns only the first matching element or null if no matches exist, efficient when you need just one result. Any() executes the query and returns a boolean indicating whether at least one result matches the condition, useful for existence checks without retrieving actual data. Each method has different performance implications; Any() is most efficient for existence checks, FirstOrDefault() is efficient for single results, and ToList() fetches all results. Understanding these methods helps optimize query performance by choosing the appropriate materialization method based on usage requirements. Overusing ToList() when only checking existence or retrieving a single result leads to unnecessary database queries and data transfer.
Q16. What is Eager Loading and how does Include work?
Eager loading is a strategy where related entities are loaded from the database at the same time as the primary entity, using the Include() method in LINQ queries. Include() tells Entity Framework to include specified navigation properties when executing the query, reducing the number of database round trips. ThenInclude() is used to include related entities of included entities, enabling navigation of relationship hierarchies. Eager loading is efficient when you know in advance which related data you need and can fetch everything in a single query. Without eager loading, accessing related entities triggers additional queries (lazy loading), which can be inefficient. Understanding when to use eager loading is crucial for query performance optimization and preventing N+1 query problems in Entity Framework applications.
Q17. How do you use Include and ThenInclude?
Include() is used in a LINQ query to eagerly load specified navigation properties from related entities. ThenInclude() chains after Include() to load related entities of already-included entities, enabling loading of nested relationships. For example, Include(u => u.Orders).ThenInclude(o => o.Products) loads Users with their Orders and each Order's Products in a single query. Multiple Include() calls load independent relationships, while ThenInclude() loads dependent relationships. Proper use of Include() and ThenInclude() prevents N+1 query problems and significantly improves query performance by reducing database round trips. Overusing Include() can fetch unnecessary data and impact performance; balance is important between fetching enough data in one query and not over-fetching.
using (var context = new ApplicationDbContext())
{
// Include related Orders and Products
var users = context.Users
.Include(u => u.Orders)
.ThenInclude(o => o.Products)
.Include(u => u.Profile)
.ToList();
// Access related data without additional queries
foreach (var user in users)
{
Console.WriteLine($"User: {user.Name}");
foreach (var order in user.Orders)
{
Console.WriteLine($" Order: {order.Id}");
foreach (var product in order.Products)
{
Console.WriteLine($" Product: {product.Title}");
}
}
}
}
Q18. What is Lazy Loading?
Lazy loading is a strategy where related entities are loaded on-demand when accessed, rather than being eagerly loaded with the primary entity. In lazy loading, accessing a navigation property triggers an additional database query to fetch related data. This is convenient for exploratory code but can lead to N+1 query problems when iterating over collections and accessing navigation properties on each item. Lazy loading requires virtual navigation properties and proxy classes that intercept property access. In EF Core, lazy loading requires installing the appropriate proxy package and explicitly enabling it. Lazy loading is useful when you don't know which related entities will be accessed ahead of time, but eager loading or explicit loading are preferred for performance-critical scenarios. Understanding lazy loading is important for identifying N+1 query problems and optimizing query performance.
Q19. What is the N+1 Query Problem?
The N+1 query problem occurs when loading a collection of N entities results in N+1 queries: one initial query to fetch the collection, then one additional query for each item to fetch related data. For example, loading 100 users and accessing their orders without eager loading triggers 101 queries (1 for users + 100 for each user's orders). This significantly impacts performance, especially with large datasets. The problem typically arises with lazy loading or explicitly loading related data in loops. Solutions include eager loading with Include(), explicit loading before iteration, or selecting only needed data in a single query. Identifying and fixing N+1 problems is crucial for application performance. Query analysis tools and database profiling can help detect N+1 patterns in production applications.
Q20. How do you fix the N+1 Query Problem?
The N+1 problem can be fixed by using eager loading with Include() to fetch all needed data in a single query, by using explicit loading to fetch related data before iteration, or by restructuring queries to select only needed data. The most common fix is using Include() to load related entities with the primary collection. Another approach is using SelectMany() to flatten relationships and project only needed properties. LINQ Select() can also be used to fetch related data directly without loading full entities. Profiling the database to identify N+1 patterns helps locate these issues. The key is understanding your data access patterns and planning queries to minimize database round trips. Fixing N+1 problems usually results in dramatic performance improvements, making this a high-impact optimization.
Q21. What are entity relationships (One-to-One, One-to-Many, Many-to-Many)?
Entity relationships represent how entities are related in the database: One-to-One relationships mean one primary entity is related to exactly one dependent entity, One-to-Many relationships mean one primary entity is related to many dependent entities, and Many-to-Many relationships mean entities from one type can be related to entities of another type in multiple ways. One-to-One is implemented with a foreign key property, One-to-Many with a collection navigation property, and Many-to-Many with a join table (in EF6) or direct join entity (in EF Core). Navigation properties enable accessing related entities from either side of the relationship. Understanding relationship types is fundamental for designing good database schemas and modeling domain objects in Entity Framework. Relationships define data integrity constraints and enable cascading deletes and other database behaviors.
Q22. How do you configure Many-to-Many relationships in EF Core?
Many-to-Many relationships in EF Core connect two entities through a join entity or are automatically configured if navigation properties are defined on both sides of the relationship. When you define collection navigation properties on both entities (Students and Courses both have collections of each other), EF Core automatically creates a join table with foreign keys to both entities. You can explicitly configure the join entity for more control using HasMany().WithMany().UsingEntity() in OnModelCreating. The join table's primary key is typically a composite of foreign keys to both related entities. Many-to-Many relationships allow querying and navigating from either entity to related entities. In EF Core 5+, Many-to-Many relationships are simplified compared to EF6 which required explicit join entities.
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection Courses { get; set; } = new List();
}
public class Course
{
public int Id { get; set; }
public string Title { get; set; }
public ICollection Students { get; set; } = new List();
}
// Configure Many-to-Many in OnModelCreating (optional for EF Core 5+)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasMany(s => s.Courses)
.WithMany(c => c.Students)
.UsingEntity(j => j.ToTable("StudentCourses"));
}
Q23. What are Navigation Properties?
Navigation properties are properties in an entity class that provide access to related entities, enabling you to navigate from one entity to related entities without explicit queries. A scalar navigation property references a single related entity (e.g., Order has a Customer property), while a collection navigation property references multiple related entities (e.g., Customer has an Orders collection). Navigation properties are virtual in lazy loading scenarios to support lazy loading proxies. They are typically defined using automatic properties with backing fields. Entity Framework uses navigation properties to determine relationships and generate appropriate SQL joins. Navigation properties must match relationship configurations; if configured incorrectly, LINQ queries using navigation properties may fail or produce incorrect results. Proper navigation properties are essential for intuitive, maintainable data access code.
Q24. What are Foreign Keys?
Foreign keys are properties that reference the primary key of another entity, establishing a relationship between two tables in the database. A foreign key property like UserId in the Order entity references the primary key of the User entity, maintaining referential integrity. Foreign keys can be explicitly defined as properties or implicitly created by Entity Framework based on navigation property naming conventions. Explicit foreign key properties (e.g., int UserId) make relationships obvious in code, while implicit foreign keys (derived from User navigation property) follow naming conventions. Foreign key constraints prevent orphaned records and maintain database consistency. Cascading deletes can be configured on foreign key relationships to automatically delete dependent records when the primary record is deleted. Understanding foreign keys is fundamental for modeling relationships in Entity Framework.
Q25. What is AsNoTracking and when should it be used?
AsNoTracking() tells Entity Framework not to track entities returned from a query, improving performance by avoiding the overhead of change detection and identity mapping. Tracked entities occupy memory and require Entity Framework to monitor changes for updates, while untracked entities are simply data transfer objects without change detection. AsNoTracking() should be used for read-only queries where you don't plan to update entities, significantly improving performance and memory usage for large result sets. It's particularly useful in reporting scenarios or public APIs returning data that won't be modified. Untracked entities cannot be directly updated; you must attach them to the context before modifications. Using AsNoTracking() appropriately is important for optimizing query performance, especially in high-throughput read-heavy scenarios.
Q26. What is Change Tracking in Entity Framework?
Change tracking is Entity Framework's mechanism for monitoring modifications to entity properties and determining which changes need to be persisted to the database. When entities are loaded from the database, Entity Framework stores original values and tracks any property changes made in memory. SaveChanges() compares current values with tracked original values to generate appropriate INSERT, UPDATE, or DELETE statements. Tracked entities have an EntityState indicating whether they are Added, Modified, Deleted, Unchanged, or Detached. Change tracking is automatic for entities loaded through DbSet queries but can be disabled with AsNoTracking() for performance. Understanding change tracking helps explain Entity Framework behavior and optimize performance in scenarios with many entity modifications. Change tracking can be inspected using ChangeTracker property on DbContext for debugging.
Q27. What is EntityState and SaveChanges()?
EntityState is an enumeration describing the state of an entity in the DbContext: Added (new entity not in database), Modified (entity from database with property changes), Deleted (entity marked for deletion), Unchanged (entity from database without changes), or Detached (entity not tracked by context). SaveChanges() executes INSERT, UPDATE, and DELETE statements based on entity states, persisting all tracked changes to the database atomically. SaveChanges() returns the number of affected rows and throws DbUpdateException on conflicts. SaveChanges() should be called after making entity modifications to persist them permanently. Understanding EntityState helps predict SaveChanges() behavior and troubleshoot issues where expected modifications aren't persisted. SaveChanges(false) in EF Core disables change tracking state validation if needed in advanced scenarios.
using (var context = new ApplicationDbContext())
{
// Add a new user (State: Added)
var newUser = new User { Name = "John", Email = "john@example.com" };
context.Users.Add(newUser);
// Retrieve and modify existing user (State: Modified after change)
var user = context.Users.FirstOrDefault(u => u.Id == 1);
user.Name = "Updated Name";
// Mark user for deletion (State: Deleted)
var userToDelete = context.Users.FirstOrDefault(u => u.Id == 2);
context.Users.Remove(userToDelete);
// Check entity states before saving
foreach (var entry in context.ChangeTracker.Entries())
{
Console.WriteLine($"{entry.Entity.GetType().Name}: {entry.State}");
}
// Persist all changes to database
int changedCount = context.SaveChanges();
Console.WriteLine($"Changes persisted: {changedCount}");
}
Q28. What are Transactions in Entity Framework?
Transactions in Entity Framework ensure that multiple database operations are executed atomically, either all succeeding or all failing together, maintaining database consistency. SaveChanges() implicitly wraps all operations in a transaction by default. Explicit transactions using BeginTransaction() provide control over transaction scope and isolation level, allowing multiple SaveChanges() calls or raw SQL commands within a single transaction. Rolling back a transaction discards all changes since the transaction began. Transactions are essential for ensuring data consistency in scenarios involving multiple related changes, preventing partial updates if an error occurs. Understanding transaction behavior prevents data integrity issues and unexpected results. Nested transactions and savepoints are supported through transaction management APIs, enabling complex scenarios with partial rollback capability.
using (var context = new ApplicationDbContext())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
// Create order and reduce inventory atomically
var order = new Order { UserId = 1, TotalAmount = 100 };
context.Orders.Add(order);
context.SaveChanges();
var product = context.Products.FirstOrDefault(p => p.Id == 1);
if (product != null && product.Quantity > 0)
{
product.Quantity--;
context.SaveChanges();
}
// Both changes succeed or both fail
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
}
}
Q29. What are Raw SQL Queries in Entity Framework?
Raw SQL queries in Entity Framework allow executing custom SQL directly against the database while still materializing results into strongly-typed entities or scalars. FromSqlRaw() and FromSqlInterpolated() (preferred for parameterized queries) execute raw SELECT queries and map results to entities. ExecuteSqlRaw() and ExecuteSqlInterpolated() execute INSERT, UPDATE, or DELETE statements and return affected row count. Raw SQL is useful for complex queries that LINQ cannot express, performance-critical operations, or when leveraging database-specific features. Care must be taken to prevent SQL injection by using parameterized queries. Raw SQL bypasses some Entity Framework features like lazy loading and automatic change tracking, requiring manual management. Raw SQL should be used sparingly; LINQ queries are preferred for most scenarios due to safety and maintainability.
Q30. How do you call Stored Procedures in Entity Framework?
Stored procedures can be called in Entity Framework using FromSqlRaw() or FromSqlInterpolated() with the EXEC command syntax. For stored procedures returning entity types, FromSqlRaw("EXEC sp_name @param1, @param2", param1Value, param2Value) materializes results into entities. For stored procedures returning scalars, ExecuteSqlRaw() executes the procedure and returns affected rows. Output parameters and return values from stored procedures are accessible through parameter objects. Mapping stored procedure results to entities requires the result set to match entity properties. Stored procedures are useful for performance-critical operations or complex business logic implemented in the database. However, LINQ queries are generally preferred due to type safety, testability, and portability across database providers. Stored procedures reduce portability since they are database-specific.
Q31. What are Database Views in Entity Framework?
Database views are virtual tables defined by SELECT queries in the database, and Entity Framework can map them to entity types just like regular tables. Views are useful for exposing computed data, aggregated results, or complex projections as if they were tables. In Entity Framework, views are configured using ToView() in OnModelCreating to specify the view name. Views are read-only in Entity Framework by default since updates must be directed to underlying tables. Views simplify complex queries by encapsulating business logic in the database layer. However, views can impact performance if complex operations are performed, and they add database complexity. Views should be used for legitimate read-only queries that truly represent virtual tables, not as a workaround for missing features.
Q32. What is Optimistic Concurrency Control?
Optimistic concurrency assumes conflicts are rare and allows multiple users to modify the same entity without locking. When saving changes, the application checks if the entity has been modified since it was loaded by comparing a concurrency token (typically a RowVersion or Timestamp column). If the entity was modified by another user, SaveChanges() throws a DbUpdateConcurrencyException, allowing the application to handle the conflict (merge, overwrite, or retry). Optimistic concurrency is efficient for scenarios with few conflicts and many concurrent users since it doesn't require database locks. Entity Framework tracks concurrency tokens and automatically includes them in UPDATE statements' WHERE clauses. Handling concurrency exceptions requires deliberate application logic to decide between overwriting or merging changes.
Q33. What is Pessimistic Concurrency Control?
Pessimistic concurrency assumes conflicts are likely and locks resources while they are being accessed, preventing other users from modifying locked entities. Database locks (READ LOCK for queries, WRITE LOCK for updates) prevent concurrent modifications during the lock duration. While preventing conflicts, pessimistic concurrency can severely impact performance and scalability, especially with many concurrent users. Pessimistic concurrency is appropriate in scenarios with frequent conflicts or high-value transactions where ensuring consistency is critical. Entity Framework doesn't directly support pessimistic locking (locking is primarily database-specific), though hints can be used in raw SQL (e.g., WITH (UPDLOCK) in SQL Server). Optimistic concurrency is generally preferred in modern applications due to better scalability; pessimistic concurrency is reserved for specific high-value transaction scenarios.
Q34. What is RowVersion or Timestamp in EF Core?
RowVersion (or Timestamp in SQL Server) is a special byte array column that automatically increments whenever a record is modified, serving as a concurrency token in optimistic concurrency scenarios. Entity Framework automatically includes the RowVersion column in UPDATE statements' WHERE clauses to ensure the entity hasn't been modified since it was loaded. If another user modified the same entity, the RowVersion won't match, and SaveChanges() throws DbUpdateConcurrencyException. RowVersion columns are configured using [Timestamp] Data Annotation or IsRowVersion() in Fluent API. RowVersion columns are database-managed and don't require application code to update them. RowVersion is the preferred concurrency token approach due to its simplicity and database support. Understanding RowVersion is essential for implementing optimistic concurrency in Entity Framework applications.
Q35. How do you handle DbUpdateConcurrencyException?
DbUpdateConcurrencyException is thrown when SaveChanges() encounters a concurrency conflict (another user modified the entity since it was loaded). Handling this exception involves catching it, accessing conflicting entity entries through the exception.Entries collection, comparing current and original values, and deciding whether to overwrite, merge, or retry. The exception provides access to current values (what the user tried to save), database values (what another user saved), and original values (what was loaded). Common strategies include overwriting the conflicting entity, prompting the user to resolve the conflict, or reloading the entity and retrying the save. Proper concurrency exception handling is critical for ensuring data integrity and providing good user experience in multi-user applications. Testing concurrency scenarios is important to ensure exception handling works correctly.
Q36. What is the Repository Pattern with Entity Framework?
The Repository pattern is an abstraction that encapsulates data access logic, providing a higher-level interface for accessing and manipulating data. A generic repository provides CRUD operations (Create, Read, Update, Delete) that work with any entity type, hiding Entity Framework complexity from business logic layers. Repositories decouple business logic from data access implementation, making code more testable and maintainable. Repositories enable unit testing by substituting mock implementations for actual database operations. Entity Framework DbContext can be abstracted within repositories, allowing business logic to remain independent of ORM details. The Repository pattern encourages dependency injection, allowing controllers and services to depend on repository interfaces rather than concrete implementations. While the Repository pattern adds abstraction overhead, it provides significant benefits in large applications by promoting clean architecture and testability.
public interface IRepository where T : class
{
T GetById(int id);
IEnumerable GetAll();
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void SaveChanges();
}
public class Repository : IRepository where T : class
{
private readonly ApplicationDbContext _context;
private readonly DbSet _dbSet;
public Repository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set();
}
public T GetById(int id) => _dbSet.Find(id);
public IEnumerable GetAll() => _dbSet.ToList();
public void Add(T entity) => _dbSet.Add(entity);
public void Update(T entity) => _dbSet.Update(entity);
public void Delete(T entity) => _dbSet.Remove(entity);
public void SaveChanges() => _context.SaveChanges();
}
Q37. What is the Unit of Work Pattern?
The Unit of Work pattern is a behavioral pattern that maintains a list of entities affected by a business transaction and coordinates writing changes to the database. A Unit of Work implementation typically provides repositories for different entity types and coordinates SaveChanges() calls across all repositories to ensure atomicity. The Unit of Work pattern is particularly useful in scenarios involving transactions across multiple entities and repositories. In Entity Framework, DbContext itself implements the Unit of Work pattern by tracking entities and coordinating SaveChanges(). A custom Unit of Work wrapper can add additional behavior and ensure consistent transaction handling. The pattern promotes clean separation between business logic and data access while ensuring ACID properties. Unit of Work is valuable in large applications with complex business logic involving multiple entity modifications.
public interface IUnitOfWork : IDisposable
{
IRepository Users { get; }
IRepository Orders { get; }
IRepository Products { get; }
void SaveChanges();
}
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
public IRepository Users { get; private set; }
public IRepository Orders { get; private set; }
public IRepository Products { get; private set; }
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
Users = new Repository(context);
Orders = new Repository(context);
Products = new Repository(context);
}
public void SaveChanges()
{
_context.SaveChanges();
}
public void Dispose()
{
_context?.Dispose();
}
}
Q38. What is Shadow Property in EF Core?
Shadow properties are properties that don't exist in the entity class but are tracked and managed by Entity Framework, existing only in the database. Shadow properties are configured using Property() or Property<T>() in Fluent API without a corresponding CLR property in the entity class. They are useful for tracking metadata like creation dates, modification dates, or concurrency tokens without exposing them as public properties. Shadow properties are accessed using the ChangeTracker or EF.Property() method rather than direct property access. Shadow properties reduce entity class size and hide implementation details from consumers. They enable tracking technical metadata without polluting entity classes with database-specific concerns. Shadow properties demonstrate Entity Framework's flexibility in modeling complex scenarios where entity classes don't directly correspond to all database columns.
Q39. What is Bulk Operations in Entity Framework?
Bulk operations are high-performance techniques for inserting, updating, or deleting large numbers of entities in a single operation, bypassing Entity Framework's tracking mechanism for efficiency. The Entity Framework Core Extensions library and similar packages provide BulkInsert(), BulkUpdate(), and BulkDelete() methods that generate efficient SQL for bulk operations. Standard Entity Framework operations require individual SaveChanges() calls for large datasets, causing performance issues. Bulk operations significantly improve performance by executing a single SQL statement, reducing database round trips and network overhead. Bulk operations bypass change tracking, so they don't populate the ChangeTracker, making them unsuitable for scenarios requiring detailed change tracking. Bulk operations are appropriate for batch processing, data imports/exports, and cleanup operations. Understanding bulk operation techniques is essential for handling large-scale data operations efficiently in Entity Framework applications.
Q40. What are Compiled Queries in Entity Framework?
Compiled queries are queries compiled once and cached for reuse, improving performance by eliminating query compilation overhead for frequently executed queries. EF Core's Compiled Queries feature allows pre-compiling LINQ queries using EF.CompileQuery() or EF.CompileAsyncQuery(), which are then executed multiple times with different parameters. Compiled queries are particularly useful for queries executed thousands of times in a loop or high-throughput scenarios. Without compilation, Entity Framework translates LINQ to SQL each time the query executes, adding overhead. Compiled queries provide measurable performance improvements, especially for simple queries where compilation overhead is significant relative to query execution time. Compiled queries require careful implementation to ensure they remain valid across different DbContext instances. Understanding when to use compiled queries is important for optimizing Entity Framework performance in high-throughput applications.
Q41. What is Split Queries in EF Core?
Split queries are a query execution strategy that executes a single LINQ query as multiple SQL queries instead of a single complex JOIN query. When Include() is used with multiple collection navigation properties, Entity Framework can execute separate queries for each collection, reducing result set size and improving performance. Split queries are configured using AsSplitQuery() on a query. Single queries with multiple JOINs can produce large result sets due to Cartesian products when joining with multiple collections. Split queries avoid this by fetching each collection separately, then materializing results into the parent entity. Split queries may execute more SQL but often provide better overall performance due to smaller result sets and reduced data transfer. Understanding split queries helps optimize queries with multiple collection includes. Split queries represent a trade-off between query complexity and result set size, useful in specific scenarios.
// Without split query - single complex JOIN
var users = context.Users
.Include(u => u.Orders)
.Include(u => u.Addresses)
.ToList();
// With split query - multiple simpler queries
var users = context.Users
.Include(u => u.Orders)
.Include(u => u.Addresses)
.AsSplitQuery()
.ToList();
Q42. What are Filtering Capabilities in EF Core?
Filtering capabilities in Entity Framework include LINQ Where() for filtering entities by conditions, HasQueryFilter() for defining default query filters at the model level, and expression-based filters for reusable filtering logic. HasQueryFilter() applies a filter automatically to all queries of an entity type, useful for implementing soft deletes or tenant-based filtering. Filters can be suppressed using IgnoreQueryFilters() for specific queries when needed. Global query filters reduce repetitive code and ensure consistent business logic across all queries. Expression-based filters provide reusable filtering predicates that can be composed in multiple queries. Filtering at the Entity Framework level translates to SQL WHERE clauses, executing filtering at the database for efficiency. Understanding filtering capabilities helps build maintainable applications with consistent data access patterns and business rule enforcement.
Q43. What is Table-per-Type (TPT) Inheritance?
Table-per-Type (TPT) inheritance maps a base entity and each derived entity to separate database tables, with the base table containing shared columns and derived tables containing type-specific columns. TPT requires joins across tables when querying polymorphic types but provides a clean schema where each table represents a distinct entity type. Queries for derived types join the base and derived tables, while queries for base types can query just the base table. TPT is useful when derived types have significantly different properties or when sharing tables between distinct types is undesirable. TPT adds query complexity and performance overhead due to required joins. EF Core supports TPT using IsAbstract() and inheritance mappings. Understanding TPT inheritance helps design effective inheritance hierarchies in Entity Framework applications.
Q44. What is Stored Queries in EF Core?
Stored queries in EF Core are reusable, typed query methods that encapsulate complex LINQ queries, promoting reusability and maintainability. Instead of writing the same complex query in multiple locations, you define a stored query method that returns IQueryable<T>, allowing further composition. Stored queries can be unit tested independently, improving code quality. Using extension methods on IQueryable<T> provides a clean API for querying, similar to a domain-specific language. Stored queries are particularly useful for frequently-used filtering, sorting, and selection logic. They promote consistency by centralizing query logic and prevent duplication. Stored queries represent a middle ground between raw repositories and simple LINQ queries, providing reusability while maintaining flexibility. Mastering stored queries improves data access layer design and maintainability.
Q45. What is Query Projection and Select() in EF Core?
Query projection using Select() transforms entities into different shapes (often DTOs or View Models) during query execution, reducing data transfer and improving performance. Instead of fetching entire entities, Select() specifies which properties to retrieve and how to structure the result. Projections translate to SQL SELECT clauses, fetching only needed columns from the database rather than entire rows. Projections are efficient because database executes filtering and shaping before returning results to the application. Anonymous types or explicit DTOs can be used as projection targets. Projections are particularly useful in read-heavy scenarios or APIs where returning full entities is unnecessary. Understanding query projection is essential for optimizing Entity Framework queries, especially in high-throughput applications or when bandwidth efficiency is important.
Q46. What are best practices for Entity Framework performance?
Entity Framework performance best practices include using AsNoTracking() for read-only queries, eagerly loading related entities with Include() to prevent N+1 queries, using projections to fetch only needed columns, batching updates/deletes for large operations, proper indexing in the database, and profiling queries to identify slow operations. Avoid materializing entire result sets into memory when possible; filter at the database level using Where() before ToList(). Use async queries (ToListAsync) to prevent blocking request threads. Consider using raw SQL for complex queries where LINQ produces inefficient SQL. Monitor Entity Framework's generated SQL using logging to identify performance issues. Cache frequently accessed data that doesn't change frequently. Database indexing, query hints, and execution plan analysis are essential for identifying bottlenecks. Performance optimization is an ongoing process requiring monitoring, profiling, and iterative improvements. Understanding these practices is essential for building scalable Entity Framework applications handling large datasets efficiently.
Q47. What is Cascade Delete in Entity Framework?
Cascade delete is a database constraint that automatically deletes dependent records when the parent record is deleted, maintaining referential integrity. In Entity Framework, cascade delete is configured using OnDelete(DeleteBehavior.Cascade) in relationship configuration. When a user is deleted, all their associated orders are automatically deleted if cascade delete is enabled. Alternative behaviors include Restrict (preventing deletion if children exist), SetNull (setting foreign key to null), and ClientCascade (deleting in application code). Cascade delete simplifies cleanup logic but requires careful consideration of business rules. Some applications intentionally disable cascade delete to preserve historical data or implement soft deletes instead. Understanding cascade delete is essential for designing database relationships correctly and preventing orphaned records.
Q48. How do you handle Model Validation in Entity Framework?
Model validation in Entity Framework can be performed using Data Annotations (attributes like [Required], [StringLength], [Range]), Fluent API validation methods, or custom IValidatableObject implementations. Entity Framework validates models before SaveChanges() by default, throwing DbEntityValidationException if validation fails. Validation errors can be inspected through the ValidationResult property. Custom validators can be created for complex business logic validations that standard validators cannot express. Model validation can be disabled using DisableValidationOnSaveChanges() if custom validation logic is implemented elsewhere. Validation errors should be meaningful and guide users toward correcting issues. Proper model validation prevents invalid data from being persisted to the database, ensuring data quality. Validation can be performed at multiple layers (client, API, business logic, database) for comprehensive data integrity protection.
Q49. What are Property Accessors (Backing Fields) in EF Core?
Property accessors and backing fields provide control over how properties are accessed and modified in Entity Framework, enabling encapsulation and validation logic. A backing field is a private field that stores the actual property value, while a property exposes the field through public get/set accessors. Using backing fields allows implementing complex get/set logic, validation, or side effects when properties are modified. Entity Framework automatically discovers backing fields following naming conventions (like _propertyName for a Property property). This pattern is useful for maintaining invariants and preventing invalid state changes. Backing fields enable rich domain models where entities enforce their own business rules rather than relying on external validation. Understanding backing fields and property accessors is essential for designing clean, maintainable entity classes.
Q50. What are Value Objects and Value Converters in EF Core?
Value objects are immutable objects that represent values (like Money, Address, Email) and are used instead of primitives for better type safety and domain modeling. Value converters in Entity Framework map value objects to database columns, enabling value objects to be persisted and retrieved correctly. Value objects are compared by value, not identity, meaning two value objects with the same data are considered equal. Value converters translate between value object representations in code and database column representations. Entity Framework supports complex type mapping and automatic conversion through HasConversion(). Using value objects improves code expressiveness, enables domain-driven design patterns, and provides strong typing for domain concepts. Value converters are configured using Fluent API, allowing sophisticated mappings between domain models and database schemas. Mastering value objects and converters enables building rich, expressive domain models in Entity Framework applications.