EF Core technical coding interview questions
EF Core technical coding interview questions
1. How do you configure a one-to-many relationship in EF Core?
Question:
How would you configure a one-to-many relationship between Author
and Book
in EF Core, where one author can have many books, but a book can only have one author?
Answer:
In EF Core, a one-to-many relationship can be configured using the HasMany
and WithOne
methods in the OnModelCreating
method.
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Author> Authors { get; set; }
public DbSet<Book> Books { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>()
.HasMany(a => a.Books)
.WithOne(b => b.Author)
.HasForeignKey(b => b.AuthorId);
}
}
2. How do you implement lazy loading in EF Core?
Question:
You need to implement lazy loading in EF Core for a scenario where each Customer
has many Orders
. How would you set this up?
Answer:
EF Core supports lazy loading using proxy objects. To enable lazy loading, you need to install the Microsoft.EntityFrameworkCore.Proxies
package and enable it in OnConfiguring
.
-
Install the necessary package:
dotnet add package Microsoft.EntityFrameworkCore.Proxies
-
Enable lazy loading in the
DbContext
:public class AppDbContext : DbContext { public DbSet<Customer> Customers { get; set; } public DbSet<Order> Orders { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder .UseSqlServer("YourConnectionString") .UseLazyLoadingProxies(); // Enables lazy loading } }
-
Ensure navigation properties are virtual:
public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public virtual ICollection<Order> Orders { get; set; } // Navigation property must be virtual } public class Order { public int OrderId { get; set; } public string OrderDetails { get; set; } public int CustomerId { get; set; } public virtual Customer Customer { get; set; } // Navigation property must be virtual }
With this configuration, EF Core will automatically load the Orders
collection when you access customer.Orders
for the first time.
3. How would you write a custom query using raw SQL in EF Core?
Question:
Write a custom query using raw SQL to fetch all customers who have placed orders worth more than $500.
Answer:
You can use FromSqlRaw
or ExecuteSqlRaw
to execute raw SQL queries in EF Core.
var customers = dbContext.Customers
.FromSqlRaw("SELECT * FROM Customers c WHERE EXISTS (SELECT 1 FROM Orders o WHERE o.CustomerId = c.CustomerId AND o.TotalAmount > 500)")
.ToList();
Here, we use a SQL subquery to find customers who have placed orders with a total amount greater than $500.
4. How do you handle concurrency in EF Core using a RowVersion
?
Question:
You have a Product
entity with a Price
property. You want to ensure that changes to the Price
are made in a way that prevents concurrency issues (e.g., two users editing the same Price
at the same time). How would you implement this in EF Core?
Answer:
You can implement optimistic concurrency control by adding a RowVersion
property to the entity, which is a byte array that EF Core automatically updates whenever the row is modified.
-
Add the
RowVersion
property to theProduct
entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } public byte[] RowVersion { get; set; } // Concurrency token }
-
Configure the
RowVersion
property inOnModelCreating
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property(p => p.RowVersion) .IsRowVersion(); // Mark as concurrency token }
-
Handle concurrency exception when saving changes:
try { dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException) { // Handle concurrency exception (e.g., prompt user to refresh or merge changes) }
When SaveChanges
is called, EF Core checks the value of RowVersion
. If the row has been modified since it was retrieved, EF Core throws a DbUpdateConcurrencyException
.
5. How do you configure a many-to-many relationship in EF Core?
Question:
You have two entities, Student
and Course
. A student can enroll in multiple courses, and a course can have multiple students. How would you configure this many-to-many relationship in EF Core?
Answer:
Starting with EF Core 5, many-to-many relationships are handled without needing to explicitly define a join entity. However, you can still configure it using HasMany
and WithMany
.
-
Define the
Student
andCourse
entities:public class Student { public int StudentId { get; set; } public string Name { get; set; } public ICollection<Course> Courses { get; set; } } public class Course { public int CourseId { get; set; } public string CourseName { get; set; } public ICollection<Student> Students { get; set; } }
-
Configure the many-to-many relationship in
OnModelCreating
:public class AppDbContext : DbContext { public DbSet<Student> Students { get; set; } public DbSet<Course> Courses { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>() .HasMany(s => s.Courses) .WithMany(c => c.Students) .UsingEntity<StudentCourse>(); // Configure the join table (optional, for explicit control) } } public class StudentCourse { public int StudentId { get; set; } public int CourseId { get; set; } public Student Student { get; set; } public Course Course { get; set; } }
In EF Core 5+, the UsingEntity
method simplifies the process by automatically creating a join table.
6. How do you handle batch updates in EF Core?
Question:
You need to update the Status
of all orders placed more than 30 days ago to "Expired". How would you achieve this using EF Core?
Answer:
EF Core does not support batch updates natively, but you can use raw SQL to perform a batch update or use third-party libraries such as EFCore.BulkExtensions to handle bulk operations efficiently.
-
Using raw SQL:
var sql = "UPDATE Orders SET Status = 'Expired' WHERE OrderDate < @p0"; dbContext.Database.ExecuteSqlRaw(sql, DateTime.UtcNow.AddDays(-30));
-
Using a third-party library (e.g., EFCore.BulkExtensions):
dbContext.Orders .Where(o => o.OrderDate < DateTime.UtcNow.AddDays(-30)) .BatchUpdate(o => new Order { Status = "Expired" });
7. How do you create and apply a migration in EF Core?
Question:
You have made changes to your model (added a new PhoneNumber
property to the Customer
entity). How would you create and apply the migration?
Answer:
-
Create the migration using the EF Core CLI:
dotnet ef migrations add AddPhoneNumberToCustomer
-
Apply the migration to the database:
dotnet ef database update
EF Core will generate migration files in the Migrations
folder and apply the changes to the database.
8. How do you optimize the performance of EF Core queries with large datasets?
Question:
You are querying a large dataset (e.g., 100,000 records). How would you optimize the performance of this query in EF Core?
Answer:
-
Use
AsNoTracking()
for read-only queries to disable change tracking, which improves performance:var products = dbContext.Products.AsNoTracking().ToList();
-
Paginate results to load data in smaller chunks:
var pageSize = 100; var pageNumber = 1; var products = dbContext.Products
.Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToList();
3. **Use `Select` for projections** to load only the required fields instead of entire entities:
```csharp
var productNames = dbContext.Products
.Where(p => p.Price > 50)
.Select(p => p.Name)
.ToList();
- Ensure indexes on frequently queried fields (e.g.,
ProductName
).
9. How do you perform a left join in EF Core using LINQ?
Question:
You have two entities: Author
and Book
. An author may have multiple books, but some authors may have no books. Write a query using LINQ to get all authors and their books, including those without books (left join).
Answer:
You can perform a left join in LINQ using DefaultIfEmpty()
to include authors without books.
var query = from author in dbContext.Authors
join book in dbContext.Books
on author.AuthorId equals book.AuthorId into booksGroup
from book in booksGroup.DefaultIfEmpty() // Left join
select new
{
AuthorName = author.Name,
BookTitle = book != null ? book.Title : "No books"
};
var result = query.ToList();
In this query, DefaultIfEmpty()
ensures that authors without books are included in the result.
10. How do you implement a stored procedure call in EF Core?
Question:
You want to execute a stored procedure named GetOrdersByCustomer
that takes a customerId
parameter and returns a list of orders. How would you implement this in EF Core?
Answer:
EF Core allows you to execute raw SQL queries, including stored procedures, using FromSqlRaw
or ExecuteSqlRaw
for non-query procedures.
-
For a stored procedure that returns data:
var customerId = 1; var orders = dbContext.Orders .FromSqlRaw("EXECUTE GetOrdersByCustomer @customerId", new SqlParameter("@customerId", customerId)) .ToList();
-
For a stored procedure that doesn't return data (non-query):
var customerId = 1; dbContext.Database.ExecuteSqlRaw("EXECUTE DeleteOrdersByCustomer @customerId", new SqlParameter("@customerId", customerId));
Ensure that your stored procedure is correctly defined in the database, and parameters are passed as SqlParameter
to avoid SQL injection.
11. How do you handle soft deletes in EF Core?
Question:
You need to implement soft deletes in EF Core where the IsDeleted
flag is set to true
instead of actually deleting a record. How would you implement this in EF Core?
Answer:
To handle soft deletes in EF Core, you can use a flag like IsDeleted
on your entity and override the SaveChanges
method to filter out soft-deleted records in queries.
-
Add an
IsDeleted
property:public class Product { public int ProductId { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } // Soft delete flag }
-
Override
SaveChanges
to set the flag instead of deleting:public class AppDbContext : DbContext { public DbSet<Product> Products { get; set; } public override int SaveChanges() { foreach (var entry in ChangeTracker.Entries().Where(e => e.Entity is Product && e.State == EntityState.Deleted)) { entry.State = EntityState.Modified; ((Product)entry.Entity).IsDeleted = true; } return base.SaveChanges(); } }
-
Filter soft-deleted records in queries:
var products = dbContext.Products .Where(p => !p.IsDeleted) .ToList();
This approach ensures that when you delete a record, it is only marked as deleted, and future queries will filter out these records.
12. How do you configure cascading updates in EF Core?
Question:
You have a Customer
entity with a related Address
entity. If the Customer
name changes, you want the Address
entity's CustomerName
property to update automatically. How would you configure cascading updates in EF Core?
Answer:
EF Core supports cascading updates by configuring relationships in the OnModelCreating
method using the OnDelete
method for cascading behavior. For cascading updates, you can manually update related entities or use custom logic.
-
Define entities:
public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public Address Address { get; set; } } public class Address { public int AddressId { get; set; } public string CustomerName { get; set; } public int CustomerId { get; set; } }
-
Set up cascading behavior (automatic propagation):
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Customer>() .HasOne(c => c.Address) .WithOne(a => a.Customer) .HasForeignKey<Address>(a => a.CustomerId) .OnDelete(DeleteBehavior.Cascade); // Cascade delete modelBuilder.Entity<Customer>() .Property(c => c.Name) .IsRequired(); }
-
Handle cascading updates manually: EF Core doesn’t support cascading updates automatically, so you'll need to update the related
Address
entity manually before saving changes:var customer = dbContext.Customers.Include(c => c.Address).FirstOrDefault(c => c.CustomerId == customerId); customer.Name = "Updated Name"; customer.Address.CustomerName = "Updated Name"; dbContext.SaveChanges();
13. How do you implement pagination in EF Core?
Question:
You have a list of Products
and need to implement pagination to fetch only a specific page of results. How would you do this in EF Core?
Answer:
Pagination can be implemented by using Skip
and Take
in LINQ, which allows you to skip a specific number of records and take a defined number.
int pageNumber = 1;
int pageSize = 10;
var products = dbContext.Products
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToList();
This will fetch the records for the given page, where pageNumber
is the current page, and pageSize
is the number of records per page. Adjust Skip
to calculate the correct number of records to skip.
14. How do you handle multi-tenant data in EF Core?
Question:
You are working with a multi-tenant application where data for each tenant should be isolated. How would you implement multi-tenancy in EF Core?
Answer:
There are multiple approaches to handle multi-tenancy in EF Core:
-
Single database, separate schema per tenant: Each tenant gets its own schema in the same database. You can configure this dynamically in the
OnModelCreating
method based on the tenant.public class AppDbContext : DbContext { private readonly string _tenantSchema; public AppDbContext(DbContextOptions<AppDbContext> options, string tenantSchema) : base(options) { _tenantSchema = tenantSchema; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema(_tenantSchema); // Set the schema dynamically } }
-
Single schema with a tenant identifier: Add a
TenantId
field to your entities and filter queries byTenantId
for multi-tenancy isolation.public class Product { public int ProductId { get; set; } public string Name { get; set; } public int TenantId { get; set; } // Tenant identifier } var tenantId = 1; var products = dbContext.Products .Where(p => p.TenantId == tenantId) .ToList();
-
Use separate
DbContext
for each tenant: You could also instantiate a differentDbContext
for each tenant if you want to isolate databases for each tenant, but this comes with more overhead.
15. How would you perform a batch insert in EF Core?
Question:
You want to insert multiple Order
entities into the database in a single operation. How would you perform this batch insert efficiently in EF Core?
Answer:
EF Core does not support batch inserts natively, but you can insert multiple entities at once using AddRange
and SaveChanges
.
var orders = new List<Order>
{
new Order { OrderDate = DateTime.Now, TotalAmount = 100 },
new Order { OrderDate = DateTime.Now, TotalAmount = 150 },
new Order { OrderDate = DateTime.Now, TotalAmount = 200 }
};
dbContext.Orders.AddRange(orders);
dbContext.SaveChanges();
This inserts all orders in one transaction, which is more efficient than inserting them one by one.
For large datasets, you can use a third-party library such as EFCore.BulkExtensions to perform bulk inserts, which can be much faster than standard AddRange
.
Certainly! Below are more EF Core technical coding interview questions with solutions, focusing on advanced use cases and concepts that experienced developers should be familiar with.
16. How do you perform conditional loading in EF Core?
Question:
You have a Customer
entity with a SalesOrders
collection. You want to load the SalesOrders
only if a specific condition (e.g., OrderDate > DateTime.Now
) is met. How would you implement conditional loading in EF Core?
Answer:
Conditional loading can be achieved using Where()
in combination with Include()
to filter the related entities. EF Core allows you to apply conditions before loading related data.
var customerId = 1;
var orders = dbContext.Customers
.Where(c => c.CustomerId == customerId)
.Include(c => c.SalesOrders.Where(o => o.OrderDate > DateTime.Now))
.FirstOrDefault();
var salesOrders = orders?.SalesOrders.ToList();
In this case, the SalesOrders
are filtered based on the condition (OrderDate > DateTime.Now
) during loading. Note that this does not fetch unrelated SalesOrders
when the condition isn't met.
17. How do you apply indexes in EF Core?
Question:
You have a User
entity with properties Username
and Email
. You want to improve query performance by indexing these properties. How would you define indexes for these properties in EF Core?
Answer:
Indexes can be applied in EF Core using the HasIndex
method inside the OnModelCreating
method.
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
public string Email { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasIndex(u => u.Username)
.HasDatabaseName("Idx_Username");
modelBuilder.Entity<User>()
.HasIndex(u => u.Email)
.HasDatabaseName("Idx_Email");
}
}
Here, indexes are added to the Username
and Email
columns, which will improve query performance for these properties. EF Core will generate the appropriate SQL commands during migration to create these indexes.
18. How do you handle a scenario where you need to query a many-to-many relationship but also filter on a property in the join table?
Question:
You have a Student
entity and a Course
entity, with a join table StudentCourse
. The StudentCourse
table also has an EnrollmentDate
property. How would you retrieve all students enrolled in courses after a specific date?
Answer:
To query many-to-many relationships with properties in the join table, you can use Join()
in LINQ, or you can navigate through the join table explicitly.
var date = DateTime.Now.AddMonths(-1); // Filter for students enrolled in the last month
var students = dbContext.Students
.Join(dbContext.StudentCourses,
student => student.StudentId,
studentCourse => studentCourse.StudentId,
(student, studentCourse) => new { student, studentCourse })
.Where(sc => sc.studentCourse.EnrollmentDate > date)
.Select(sc => sc.student)
.ToList();
In this query, we explicitly join Students
and StudentCourses
using LINQ. The EnrollmentDate
property from the join table is used to filter the results.
19. How do you handle data seeding in EF Core?
Question:
You need to seed initial data (like default Roles
and Users
) when the database is created or migrated. How would you implement data seeding in EF Core?
Answer:
EF Core provides an easy way to seed data through the HasData
method in the OnModelCreating
method.
-
Seeding data for
Roles
andUsers
:public class AppDbContext : DbContext { public DbSet<Role> Roles { get; set; } public DbSet<User> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Role>().HasData( new Role { RoleId = 1, Name = "Admin" }, new Role { RoleId = 2, Name = "User" } ); modelBuilder.Entity<User>().HasData( new User { UserId = 1, Username = "admin", RoleId = 1 }, new User { UserId = 2, Username = "user", RoleId = 2 } ); } }
-
Running the migration: After adding the seed data, you can apply the migration:
dotnet ef migrations add SeedInitialData dotnet ef database update
EF Core will automatically insert the seed data during the database creation or migration process.
20. How do you handle changes to the model (e.g., adding a new property) after seeding data?
Question:
You have seeded some initial data, but you need to update the model by adding a new property (IsActive
) to an existing User
entity. How would you handle this change in EF Core after seeding data?
Answer:
If you modify your model, EF Core will require a migration to apply the schema changes. To handle the seeding with the new model property:
-
Add the
IsActive
property to theUser
entity:public class User { public int UserId { get; set; } public string Username { get; set; } public int RoleId { get; set; } public bool IsActive { get; set; } // New property }
-
Update the seeding logic to include the
IsActive
property:modelBuilder.Entity<User>().HasData( new User { UserId = 1, Username = "admin", RoleId = 1, IsActive = true }, new User { UserId = 2, Username = "user", RoleId = 2, IsActive = true } );
-
Create and apply a migration:
dotnet ef migrations add AddIsActiveToUser dotnet ef database update
EF Core will apply the schema change to the database, including the new IsActive
property and the updated seed data.
21. How do you configure a unique constraint on a property or a set of properties in EF Core?
Question:
You need to ensure that the combination of Username
and Email
in the User
entity is unique. How would you configure this in EF Core?
Answer:
EF Core supports defining unique constraints using HasIndex
in the OnModelCreating
method. You can specify a unique constraint on a single column or a combination of columns.
- Configure a unique constraint on
Username
andEmail
:public class User { public int UserId { get; set; } public string Username { get; set; } public string Email { get; set; } } public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>() .HasIndex(u => new { u.Username, u.Email }) .IsUnique(); } }
This will ensure that the combination of Username
and Email
is unique in the database.
22. How do you manage schema migrations when working with multiple environments (e.g., development, staging, production)?
Question:
You have different environments (development, staging, production), and you need to manage database migrations across these environments. How would you handle this scenario in EF Core?
Answer:
To manage migrations across different environments, you can use separate connection strings for each environment and control when migrations are applied by using the dotnet ef
CLI or programmatically.
-
Use environment-specific connection strings: Store connection strings in
appsettings.Development.json
,appsettings.Staging.json
, andappsettings.Production.json
.// appsettings.Development.json { "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=DevDb;Trusted_Connection=True;" } }
-
Apply migrations to specific environments: You can specify the connection string when running the migration commands for different environments:
dotnet ef migrations add InitialMigration --environment Development dotnet ef database update --environment Production
-
Programmatically applying migrations: In production, you might want to apply migrations programmatically when the application starts (after validating that migrations are needed).
public class Program { public static void Main(string[] args) { var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); dbContext.Database.Migrate(); // Automatically apply migrations } host.Run(); } }
23. How would you configure a custom database function in EF Core?
Question:
You need to use a custom SQL function `GetFull
Name` in EF Core, which concatenates a user's first and last names. How would you configure and use this custom function?
Answer:
EF Core allows you to map custom database functions to LINQ queries using HasDbFunction
.
-
Define the custom function in the
OnModelCreating
method:public class AppDbContext : DbContext { public DbSet<User> Users { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDbFunction(() => GetFullName(default, default)); } public static string GetFullName(string firstName, string lastName) { throw new NotImplementedException("This function will be translated to SQL."); } }
-
Use the custom function in a query:
var fullName = dbContext.Users .Where(u => EF.Functions.GetFullName(u.FirstName, u.LastName) == "John Doe") .ToList();
EF Core will translate GetFullName
into the corresponding SQL function when executing the query.
Certainly! Below are even more EF Core technical coding interview questions with answers, targeting more advanced scenarios and real-world use cases. These are geared toward experienced developers who need to demonstrate deep knowledge of EF Core.
24. How do you handle database transactions in EF Core?
Question:
You want to ensure that two operations—updating a Product
and inserting an Order
—either both succeed or both fail. How would you handle this in EF Core using transactions?
Answer:
EF Core supports transactions natively through IDbContextTransaction
. You can use BeginTransaction()
to explicitly control transactions.
using var transaction = dbContext.Database.BeginTransaction();
try
{
// Operation 1: Update Product
var product = dbContext.Products.FirstOrDefault(p => p.ProductId == 1);
product.Stock -= 10;
dbContext.SaveChanges();
// Operation 2: Insert Order
var order = new Order { ProductId = 1, Quantity = 10 };
dbContext.Orders.Add(order);
dbContext.SaveChanges();
// Commit transaction if both operations succeed
transaction.Commit();
}
catch (Exception)
{
// Rollback transaction if any operation fails
transaction.Rollback();
throw;
}
In this example, if any operation fails, the transaction is rolled back to ensure data consistency.
25. How do you prevent or resolve circular references in EF Core during serialization?
Question:
You have two entities, Author
and Book
, with a circular reference (i.e., Author
has a collection of Books
and Book
has an Author
property). How do you prevent infinite loops during serialization (e.g., when returning these entities as JSON)?
Answer:
Circular references can be prevented during serialization by using the JsonIgnore
attribute or the ReferenceHandler
property in System.Text.Json
configuration.
-
Use
JsonIgnore
attribute: Apply theJsonIgnore
attribute on the navigation property that causes the circular reference:public class Author { public int AuthorId { get; set; } public string Name { get; set; } public ICollection<Book> Books { get; set; } } public class Book { public int BookId { get; set; } public string Title { get; set; } [JsonIgnore] // Prevent circular reference public Author Author { get; set; } }
-
Using
ReferenceHandler
in theProgram.cs
file (for .NET 6 and beyond):public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; }); var app = builder.Build(); app.MapControllers(); app.Run(); } }
This will prevent infinite recursion during serialization by ignoring circular references.
26. How would you implement optimistic concurrency control in EF Core?
Question:
You have a Product
entity with a Version
property that is an integer. You want to implement optimistic concurrency control to ensure that a Product
is not updated by multiple users simultaneously. How would you implement this in EF Core?
Answer:
Optimistic concurrency control can be implemented using a row version (e.g., a Timestamp
or Version
property) in EF Core. This property is marked as a concurrency token, and EF Core will automatically check if the value has changed during an update operation.
-
Define the
Version
property in your entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } [Timestamp] // Mark as concurrency token public byte[] Version { get; set; } // Row version }
-
Configure the
Version
property inOnModelCreating
(optional, EF Core can also detect it automatically):protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property(p => p.Version) .IsRowVersion(); }
-
Handle concurrency exceptions: If another user has updated the product in the meantime, EF Core will throw a
DbUpdateConcurrencyException
. You can catch and handle it as follows:try { dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { var entry = ex.Entries.Single(); var clientValue = (byte[])entry.Property("Version").CurrentValue; var databaseValue = (byte[])entry.Property("Version").OriginalValue; // Handle concurrency conflict, e.g., reload the entity or notify the user }
In this example, if another user updates the Product
in between, the DbUpdateConcurrencyException
is triggered, and you can handle it accordingly (e.g., reload the entity or prompt the user for resolution).
27. How do you deal with loading large datasets in EF Core efficiently?
Question:
You are querying a large dataset (e.g., thousands of Products
). How can you improve performance when loading this data into memory using EF Core?
Answer:
When dealing with large datasets, several strategies can improve performance in EF Core:
-
Use
AsNoTracking()
: If you do not need to track changes (e.g., for read-only operations), useAsNoTracking()
to disable change tracking, improving performance.var products = dbContext.Products.AsNoTracking().ToList();
-
Paging: For large result sets, use paging to load data in chunks instead of all at once.
int pageNumber = 1; int pageSize = 100; var products = dbContext.Products .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToList();
-
Select Only Required Columns: Avoid loading unnecessary columns by selecting only the needed fields.
var productNames = dbContext.Products .Where(p => p.Price > 100) .Select(p => p.Name) .ToList();
-
Asynchronous Queries: Use
async
methods to avoid blocking the thread, especially when dealing with large amounts of data.var products = await dbContext.Products.ToListAsync();
-
Batch Queries: If you need to process a large dataset, consider fetching the data in smaller batches and processing them one at a time.
28. How do you handle soft deletes and filtering in EF Core?
Question:
You have a Product
entity with an IsDeleted
flag to indicate soft deletes. You want to filter out the soft-deleted records globally. How do you implement this in EF Core?
Answer:
To filter out soft-deleted records globally, you can use a global query filter in EF Core.
-
Define the
IsDeleted
flag in the entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } // Soft delete flag }
-
Configure the global query filter in
OnModelCreating
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .HasQueryFilter(p => !p.IsDeleted); // Global filter for soft deletes }
Now, any query to Products
will automatically exclude soft-deleted records unless explicitly overridden.
-
Override the filter for a specific query (if needed):
var allProductsIncludingDeleted = dbContext.Products .IgnoreQueryFilters() // Ignores global query filters .Where(p => p.IsDeleted) .ToList();
This way, soft deletes are managed efficiently without needing to manually filter out IsDeleted
in each query.
29. How do you manage relationships when migrating from one-to-many to many-to-many in EF Core?
Question:
You initially designed a one-to-many relationship between Student
and Course
, but now you need to refactor it to a many-to-many relationship. How would you implement this change in EF Core?
Answer:
In EF Core, changing a one-to-many relationship to a many-to-many relationship requires creating a join entity for the many-to-many relationship.
-
Initial One-to-Many Relationship:
public class Student { public int StudentId { get; set; } public string Name { get; set; } public int CourseId { get; set; } // Foreign key public Course Course { get; set; } } public class Course { public int CourseId { get; set; } public string CourseName { get; set; } public ICollection<Student> Students { get; set; } }
-
Refactor to Many-to-Many Relationship: EF Core 5.0+ supports direct many-to-many relationships without needing an explicit join entity. You just define a
DbSet
and EF Core automatically manages the join table.public class Student { public int StudentId { get; set; } public string Name { get; set; } public ICollection<Course> Courses { get; set; } } public class Course { public int CourseId { get; set; } public string CourseName {
get; set; } public ICollection Students { get; set; } }
3. **Migration Changes**:
After refactoring your model, run a migration to update the schema.
```bash
dotnet ef migrations add RefactorToManyToMany
dotnet ef database update
EF Core will create a join table behind the scenes (e.g., StudentCourse
) to manage the many-to-many relationship.
30. How do you handle cascading deletes in EF Core?
Question:
You have a Blog
entity that contains multiple Posts
. When a Blog
is deleted, you want all related Posts
to be deleted automatically. How do you configure cascading deletes in EF Core?
Answer:
EF Core supports cascading deletes, which can be configured using the OnDelete
method in OnModelCreating
.
-
Define the entities:
public class Blog { public int BlogId { get; set; } public string Title { get; set; } public ICollection<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }
-
Configure cascading deletes in
OnModelCreating
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Post>() .HasOne(p => p.Blog) .WithMany(b => b.Posts) .OnDelete(DeleteBehavior.Cascade); // Cascade delete configuration }
Now, when a Blog
is deleted, all related Posts
will be automatically deleted as well.
Here are more EF Core technical coding interview questions for experienced developers:
31. How do you implement custom queries that are not supported by EF Core directly?
Question:
You want to perform a custom query, such as using a stored procedure or custom SQL, that EF Core does not directly support. How would you implement this?
Answer:
EF Core allows executing raw SQL queries or stored procedures via FromSqlRaw
or ExecuteSqlRaw
. Here's how you can handle this:
-
Executing a Raw SQL Query (SELECT): Use
FromSqlRaw
to execute a custom SQL query and return the result.var products = dbContext.Products .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100) .ToList();
-
Executing a Stored Procedure: If you have a stored procedure that returns data, you can call it like this:
var products = dbContext.Products .FromSqlRaw("EXEC GetProductsByPrice @p0", 100) .ToList();
-
Non-Query SQL Execution (UPDATE, DELETE, etc.): Use
ExecuteSqlRaw
for commands that do not return data (e.g.,UPDATE
,DELETE
).dbContext.Database.ExecuteSqlRaw("UPDATE Products SET Price = Price * 1.1 WHERE Category = 'Electronics'");
-
Stored Procedure with Parameters: You can pass parameters using the
FromSqlRaw
orExecuteSqlRaw
method:var productId = 1; var result = dbContext.Products .FromSqlRaw("EXEC GetProductDetails @p0", productId) .ToList();
EF Core will map the results of the query to the appropriate entity class.
32. What is the difference between SaveChanges()
and SaveChangesAsync()
in EF Core?
Question:
Explain the difference between SaveChanges()
and SaveChangesAsync()
, and when would you use each one?
Answer:
SaveChanges()
is a synchronous method that blocks the calling thread until all changes are persisted to the database. In contrast, SaveChangesAsync()
is an asynchronous method that runs the same operation but does not block the thread while waiting for the database operations to complete.
- Use
SaveChanges()
:- When performing short-running database operations.
- In console or desktop applications where asynchronous execution is not critical.
- Use
SaveChangesAsync()
:- In web applications (e.g., ASP.NET Core), where it is essential to avoid blocking the thread, especially when handling many requests simultaneously.
- In scenarios where responsiveness and scalability are important, particularly with long-running queries or high-latency databases.
Example:
// Synchronous
dbContext.SaveChanges();
// Asynchronous
await dbContext.SaveChangesAsync();
33. How would you implement pagination in a large dataset using EF Core?
Question:
You need to implement pagination for a large dataset of Products
to avoid loading everything into memory at once. How would you achieve this in EF Core?
Answer:
To implement pagination in EF Core, you can use the Skip()
and Take()
methods to fetch a subset of data for each page. This is especially useful when dealing with large datasets to reduce memory consumption and improve performance.
-
Example of Pagination:
public async Task<IEnumerable<Product>> GetProducts(int pageNumber, int pageSize) { return await dbContext.Products .Skip((pageNumber - 1) * pageSize) // Skip items before the current page .Take(pageSize) // Take the number of items for the current page .ToListAsync(); }
-
Handling Pagination Metadata: You can also return metadata (total count, total pages, etc.) to the client along with the paged data:
public async Task<PagedResult<Product>> GetProducts(int pageNumber, int pageSize) { var totalCount = await dbContext.Products.CountAsync(); var products = await dbContext.Products .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PagedResult<Product> { Items = products, TotalCount = totalCount, TotalPages = (int)Math.Ceiling((double)totalCount / pageSize) }; } public class PagedResult<T> { public IEnumerable<T> Items { get; set; } public int TotalCount { get; set; } public int TotalPages { get; set; } }
This ensures that only the necessary data is loaded for each page.
34. How would you implement a "soft delete" strategy with EF Core for related entities?
Question:
You have a Customer
entity and a related Order
entity. When you mark a Customer
as deleted (soft delete), how do you ensure that all related Order
records are also marked as deleted?
Answer:
You can use a combination of soft deletes and cascading soft delete logic. This involves marking the related Order
entities as deleted when the Customer
is soft deleted.
-
Add an
IsDeleted
flag to both entities:public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } // Soft delete flag public ICollection<Order> Orders { get; set; } } public class Order { public int OrderId { get; set; } public int CustomerId { get; set; } public bool IsDeleted { get; set; } // Soft delete flag public Customer Customer { get; set; } }
-
Set up cascading soft delete:
When deleting a
Customer
, you can mark all relatedOrders
as deleted.public void SoftDeleteCustomer(int customerId) { var customer = dbContext.Customers .Include(c => c.Orders) // Load related orders .FirstOrDefault(c => c.CustomerId == customerId); if (customer != null) { customer.IsDeleted = true; foreach (var order in customer.Orders) { order.IsDeleted = true; } dbContext.SaveChanges(); } }
In this example, when a Customer
is marked as deleted, all related Orders
are also marked as deleted.
35. How do you optimize complex queries in EF Core?
Question:
You have a complex query involving multiple joins, aggregations, and filtering, and the query is running slowly. How would you optimize this in EF Core?
Answer:
Optimizing complex queries in EF Core can involve several strategies:
-
Use
AsNoTracking()
for read-only queries:
If you do not need change tracking, disable it to improve performance.var result = dbContext.Orders .AsNoTracking() .Where(o => o.CustomerId == customerId) .ToList();
-
Select only the required columns:
Instead of fetching entire entities, select only the columns you need.var result = dbContext.Orders .Where(o => o.CustomerId == customerId) .Select(o => new { o.OrderId, o.TotalAmount }) .ToList();
-
Break the query into smaller parts:
If the query involves multiple steps or subqueries, break it into smaller, more manageable queries that are easier to optimize.var orders = dbContext.Orders .Where(o => o.CustomerId == customerId) .ToList(); var orderItems = dbContext.OrderItems .Where(oi => orders.Select(o => o.OrderId).Contains(oi.OrderId)) .ToList();
-
Use indexes on frequently queried columns:
Ensure that the database has indexes on columns involved in filtering (WHERE
clauses) or joining operations. -
Consider database-level optimization:
Sometimes, EF Core cannot fully optimize certain queries, especially those involving complex joins or aggregations. In such cases, using raw SQL or stored procedures can be beneficial.var result = dbContext.Products .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100) .ToList();
36. How do you handle transactions when using SaveChanges()
for multiple entities in EF Core?
Question:
You need to save changes to multiple entities (Customer
, Order
, and Product
) in a single transaction. How would you handle this to ensure all changes are persisted atomically?
Answer:
To handle transactions that involve multiple entities, you can use the TransactionScope
or IDbContextTransaction
provided by EF Core.
-
Using
IDbContextTransaction
:using (var transaction = dbContext.Database.BeginTransaction()) { try { // Modify entities dbContext.Customers.Add(new Customer { Name = "John Doe" }); dbContext.Orders.Add(new Order { CustomerId = 1, OrderDate = DateTime.Now }); dbContext.Products.Add(new Product { Name = "Laptop", Price = 1200 }); // Save all changes in one transaction dbContext.SaveChanges(); // Commit transaction transaction.Commit(); } catch (Exception) { // Rollback in case of failure transaction.Rollback(); throw; } }
In this example, all changes are committed or rolled back as a unit, ensuring data consistency across the different entities.
Here are additional advanced EF Core technical coding interview questions for experienced developers:
37. How do you handle concurrency conflicts in EF Core?
Question:
You are working on a multi-user application where two users might attempt to update the same record simultaneously. How do you handle concurrency conflicts in EF Core?
Answer:
EF Core provides optimistic concurrency handling, where a conflict occurs when two users try to update the same record at the same time. This can be resolved using a row version or timestamp field that tracks changes.
-
Add a
RowVersion
field to your entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } // RowVersion property for concurrency control [Timestamp] public byte[] RowVersion { get; set; } }
-
Handle concurrency exceptions in your code:
When a conflict occurs, EF Core will throw a
DbUpdateConcurrencyException
.try { dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { // Handle concurrency conflict foreach (var entry in ex.Entries) { if (entry.State == EntityState.Modified) { // Optionally, reload the entity and merge changes entry.OriginalValues.SetValues(entry.GetDatabaseValues()); } } }
By marking the RowVersion
property with the [Timestamp]
attribute, EF Core will automatically check this column when saving the entity to ensure no conflicting updates have been made.
38. What is the role of ValueGeneratedOnAdd()
and ValueGeneratedOnUpdate()
in EF Core?
Question:
You need to configure a property in your entity to be automatically generated on insert and updated on every update. How would you achieve this in EF Core?
Answer:
ValueGeneratedOnAdd()
and ValueGeneratedOnUpdate()
are methods in EF Core used to configure how a property value is generated by the database.
ValueGeneratedOnAdd()
: This tells EF Core that the value of the property will be generated when the entity is added (e.g., an identity column or GUID).ValueGeneratedOnUpdate()
: This tells EF Core that the value of the property will be updated every time the entity is updated (e.g., a timestamp or version number).
-
Example: Auto-generated value on insert and update:
public class Order { public int OrderId { get; set; } public string OrderNumber { get; set; } // A column that is generated on insert and updated on every update public DateTime LastModified { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Order>() .Property(o => o.LastModified) .ValueGeneratedOnAddOrUpdate(); // Value generated on both add and update }
In this example, LastModified
will be automatically populated on insert and updated every time the entity is updated.
39. How do you prevent N+1 query problems in EF Core?
Question:
You notice that your application is making N+1 queries for related data, resulting in performance issues. How do you prevent this in EF Core?
Answer:
The N+1 query problem occurs when EF Core generates an additional query for each related entity, which can result in excessive database calls. This issue can be mitigated using eager loading (via Include()
) or explicit loading.
-
Eager Loading: Use
Include()
to load related entities in a single query.var products = dbContext.Products .Include(p => p.Category) // Include related Category data .ToList();
-
Eager Loading with Multiple Relations:
var orders = dbContext.Orders .Include(o => o.Customer) // Include Customer relation .Include(o => o.OrderItems) // Include OrderItems relation .ThenInclude(oi => oi.Product) // Include Product inside OrderItems .ToList();
-
Use
.ToList()
as early as possible to limit multiple queries and reduce the time complexity.var products = dbContext.Products .Include(p => p.Supplier) .ToList(); // The Include() should be used before ToList() to reduce N+1 problems
By eager loading, EF Core will generate a single SQL query with JOINs to retrieve the data, which reduces unnecessary database round trips.
40. How do you implement custom conventions in EF Core?
Question:
You want to automatically apply a specific convention to all entities in your model (e.g., automatically naming all string columns with a max length of 255 characters). How do you implement this?
Answer:
EF Core allows you to implement custom conventions by overriding OnModelCreating
and using the ModelBuilder
API.
-
Example: Apply a max length convention to all string properties:
protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var entity in modelBuilder.Model.GetEntityTypes()) { foreach (var property in entity.GetProperties()) { if (property.ClrType == typeof(string)) { property.SetMaxLength(255); // Apply max length of 255 to all string properties } } } }
-
Applying a custom naming convention for tables:
protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var entity in modelBuilder.Model.GetEntityTypes()) { entity.SetTableName(entity.GetTableName().ToLowerInvariant()); // Convert all table names to lowercase } }
These conventions are applied to all entities automatically when the model is created, allowing for easier maintenance and standardization of naming conventions.
41. How do you handle multi-tenancy with EF Core?
Question:
You need to support a multi-tenant application where each tenant has its own database. How would you manage multi-tenancy with EF Core?
Answer:
In a multi-tenant application, you can handle tenant isolation at the database level, schema level, or table level. The approach depends on the requirements for tenant isolation.
-
Database per tenant: Each tenant has its own database. You can dynamically switch the database connection at runtime based on the tenant.
public class ApplicationDbContext : DbContext { private readonly string _tenantConnectionString; public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, string tenantConnectionString) : base(options) { _tenantConnectionString = tenantConnectionString; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(_tenantConnectionString); // Use tenant-specific database connection } }
-
Schema per tenant: If you're using the same database but different schemas for each tenant, you can configure the
DbContext
to use different schemas for different tenants.public class ApplicationDbContext : DbContext { private readonly string _tenantSchema; public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, string tenantSchema) : base(options) { _tenantSchema = tenantSchema; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema(_tenantSchema); // Configure schema based on tenant } }
-
Tenant identification: The tenant can be identified based on a header, URL segment, or user session. Once identified, you configure the context to use the appropriate database or schema.
public class TenantProvider { public string GetTenantConnectionString(string tenantId) { // Return connection string based on the tenant } }
This allows you to isolate tenants either at the database level, schema level, or even table level, depending on the architecture and isolation requirements.
42. What is the purpose of HasQueryFilter
in EF Core?
Question:
You want to automatically apply a global filter to all queries in EF Core, such as only retrieving active records. How do you implement this?
Answer:
EF Core provides the HasQueryFilter
method to define global filters that apply to all queries for a given entity type.
-
Example: Implementing a global "IsDeleted" filter:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .HasQueryFilter(p => !p.IsDeleted); // Automatically exclude deleted products }
-
Effect of Query Filters: When you perform queries on
Product
, EF Core will automatically apply theIsDeleted
filter.var activeProducts = dbContext.Products.ToList(); // The query will automatically exclude IsDeleted = true
This is useful for scenarios such as soft deletes, where you want to exclude soft-deleted records globally without having to manually filter them out in each query.
43. How do you handle schema migrations when working with multiple databases in EF Core?
Question:
Your application needs to handle migrations for multiple databases (e.g., one for tenants and one for shared resources). How would you handle schema migrations for multiple databases in EF Core?
Answer:
EF Core allows you to work with multiple databases by configuring each database and applying migrations separately.
-
Create multiple contexts for each database:
public class TenantDbContext : DbContext { public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options) { } } public class SharedDbContext : DbContext { public SharedDbContext(DbContextOptions<SharedDbContext> options) : base(options) { } }
-
Applying migrations for each database:
- First, ensure that each context has its own migration configuration.
- Use the
dotnet ef migrations add
anddotnet ef database update
commands for each context.
# Apply migrations for the tenant database dotnet ef migrations add TenantMigration --context TenantDbContext dotnet ef database update --context TenantDbContext # Apply migrations for the shared database dotnet ef migrations add SharedMigration --context SharedDbContext dotnet ef database update --context SharedDbContext
EF Core will handle the migration and schema updates separately for each database.
Here are more advanced EF Core technical coding interview questions for experienced developers:
44. What is the difference between SaveChanges()
and SaveChangesAsync()
in EF Core?
Question:
What is the difference between SaveChanges()
and SaveChangesAsync()
in EF Core, and when would you use one over the other?
Answer:
-
SaveChanges()
: This method is synchronous, meaning it will block the thread until the database operation is completed. It is suitable for scenarios where the database operations are fast or you don’t need to worry about scalability.dbContext.SaveChanges(); // Synchronous
-
SaveChangesAsync()
: This method is asynchronous, meaning it doesn’t block the thread and allows for better scalability, especially in high-concurrency scenarios. It's recommended in web applications to avoid blocking threads and improve responsiveness.await dbContext.SaveChangesAsync(); // Asynchronous
When to use:
Use SaveChangesAsync()
when you are working in asynchronous methods (e.g., web API or UI applications) to prevent blocking the UI thread or the request thread.
45. Explain the difference between eager loading, lazy loading, and explicit loading in EF Core.
Question:
What are the differences between eager loading, lazy loading, and explicit loading, and when should you use each?
Answer:
-
Eager Loading:
Eager loading loads related entities as part of the initial query using theInclude()
method. It is useful when you know you'll need the related data upfront.var orders = dbContext.Orders.Include(o => o.OrderItems).ToList();
- Use case: When you need related data immediately and want to avoid multiple queries.
-
Lazy Loading:
Lazy loading automatically loads related entities when you access the navigation property. It requires enabling lazy loading proxies in theDbContext
configuration.public class Order { public ICollection<OrderItem> OrderItems { get; set; } }
- Use case: When you don’t know if you need the related data and want it to load on-demand.
Configuration: Enable lazy loading by installing the
Microsoft.EntityFrameworkCore.Proxies
package and settingUseLazyLoadingProxies()
:optionsBuilder.UseLazyLoadingProxies();
-
Explicit Loading:
Explicit loading allows you to load related entities manually after the initial query is executed usingLoad()
orThenInclude()
.var order = dbContext.Orders.Find(1); dbContext.Entry(order).Collection(o => o.OrderItems).Load();
- Use case: When you need more control over when related data is loaded.
46. What is the ValueGeneratedOnAddOrUpdate()
method in EF Core, and how do you use it?
Question:
What is the purpose of the ValueGeneratedOnAddOrUpdate()
method in EF Core, and how do you use it?
Answer:
The ValueGeneratedOnAddOrUpdate()
method is used to configure a property that should be generated by the database both on insert and update. This is useful for properties like a timestamp or version number that should be updated every time an entity is updated.
-
Example: Using
ValueGeneratedOnAddOrUpdate()
:public class Product { public int ProductId { get; set; } public string Name { get; set; } // Timestamp or version property public DateTime LastModified { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property(p => p.LastModified) .ValueGeneratedOnAddOrUpdate(); // Automatically set and updated }
- Use case: For properties like
LastModified
or a version number that is automatically updated on both add and update operations.
- Use case: For properties like
47. How do you configure a composite primary key in EF Core?
Question:
How would you configure a composite primary key for an entity in EF Core?
Answer:
In EF Core, you can configure a composite primary key by using the HasKey()
method in the OnModelCreating
method.
-
Example: Configuring a composite primary key:
public class OrderItem { public int OrderId { get; set; } public int ProductId { get; set; } public int Quantity { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<OrderItem>() .HasKey(oi => new { oi.OrderId, oi.ProductId }); // Composite primary key }
- Use case: When you need a composite key that involves more than one property.
48. How do you handle circular references in EF Core?
Question:
You have entities that reference each other, creating a circular reference (e.g., Order
references Customer
and Customer
references Order
). How do you handle this in EF Core?
Answer:
Circular references can be problematic when serializing entities, as they may cause infinite loops. EF Core has options to manage these references:
-
Use
JsonIgnore
attribute: If you don’t want to serialize the circular references in APIs, useJsonIgnore
to break the circular reference.public class Order { public int OrderId { get; set; } public Customer Customer { get; set; } } public class Customer { public int CustomerId { get; set; } [JsonIgnore] // Prevent serialization of the circular reference public ICollection<Order> Orders { get; set; } }
-
Configure the model to handle circular references: Use the
ReferenceHandling
configuration to control the handling of circular references globally for JSON serialization in your application.services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve; });
- Use case: When serializing data for web APIs, breaking circular references helps to avoid stack overflow errors.
49. How do you implement custom database functions or stored procedures in EF Core?
Question:
You need to call a custom stored procedure or function from EF Core. How would you implement that?
Answer:
EF Core provides several ways to call raw SQL functions or stored procedures, either through FromSqlRaw()
or ExecuteSqlRaw()
methods.
-
Calling a stored procedure with
FromSqlRaw()
:public class Product { public int ProductId { get; set; } public string Name { get; set; } } var products = dbContext.Products .FromSqlRaw("EXEC GetProducts") // Call stored procedure .ToList();
-
Executing a stored procedure with
ExecuteSqlRaw()
:dbContext.Database.ExecuteSqlRaw("EXEC UpdateProductPrice {0}, {1}", productId, newPrice);
-
Mapping stored procedure results: You can map stored procedure results to entities or raw types by using
FromSqlRaw()
with LINQ projections.var results = dbContext.Products .FromSqlRaw("EXEC GetProductsWithDetails") .Select(p => new { p.ProductId, p.Name, p.Price }) .ToList();
-
Executing scalar values:
var total = dbContext.Database .ExecuteSqlRaw("SELECT SUM(Price) FROM Products");
50. What are shadow properties in EF Core?
Question:
What are shadow properties in EF Core, and how do you use them?
Answer:
Shadow properties are properties that are not defined in the entity class but are still tracked by EF Core and stored in the database. They can be useful for scenarios such as auditing or tracking metadata.
-
Example: Defining a shadow property:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property<DateTime>("CreatedDate"); // Shadow property }
- Access shadow property:
var createdDate = dbContext.Entry(product).Property("CreatedDate").CurrentValue;
- Use case: For scenarios like storing audit data or additional metadata without modifying your entity class.
Here are more advanced EF Core technical coding interview questions for experienced developers:
51. How do you handle transactions in EF Core?
Question:
How do you manage transactions in EF Core, and what are the different ways to work with transactions?
Answer:
EF Core provides several ways to handle transactions, either automatically through SaveChanges()
or manually using IDbContextTransaction
.
-
Automatic Transactions (Default):
EF Core automatically wraps allSaveChanges()
calls in a transaction. If the save is successful, the transaction is committed; if any exception occurs, the transaction is rolled back.dbContext.SaveChanges(); // Automatic transaction management
-
Manual Transactions:
For more control, you can manually start, commit, or roll back transactions usingIDbContextTransaction
. This is useful when you want to execute multiple operations in a single transaction.using (var transaction = dbContext.Database.BeginTransaction()) { try { // Perform multiple database operations dbContext.SaveChanges(); transaction.Commit(); // Commit the transaction } catch { transaction.Rollback(); // Rollback in case of error } }
-
Async Transactions:
EF Core also supports async transaction handling. TheBeginTransactionAsync()
method is used when working with async code.using (var transaction = await dbContext.Database.BeginTransactionAsync()) { try { await dbContext.SaveChangesAsync(); await transaction.CommitAsync(); // Commit the transaction } catch { await transaction.RollbackAsync(); // Rollback in case of error } }
Use Case: Use manual transactions when you need multiple operations (e.g., multiple SaveChanges()
calls or other database operations) to be part of the same atomic transaction.
52. How do you configure a one-to-many relationship in EF Core?
Question:
How do you configure a one-to-many relationship between two entities in EF Core?
Answer:
A one-to-many relationship occurs when one entity has multiple related entities, and the related entities have a foreign key to the primary entity. In EF Core, this is configured using the HasOne()
and WithMany()
methods in the OnModelCreating
method.
-
Example: Configuring a one-to-many relationship:
public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public ICollection<Order> Orders { get; set; } } public class Order { public int OrderId { get; set; } public int CustomerId { get; set; } public Customer Customer { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Order>() .HasOne(o => o.Customer) // Each Order has one Customer .WithMany(c => c.Orders) // Each Customer can have many Orders .HasForeignKey(o => o.CustomerId); // The foreign key on Order }
- Use case: When a
Customer
can have multipleOrders
, but eachOrder
is related to only oneCustomer
.
- Use case: When a
53. How do you configure inheritance with EF Core?
Question:
How do you configure inheritance in EF Core, and what strategies are available for mapping inheritance to the database?
Answer:
EF Core supports three strategies for handling inheritance in the database:
-
Table per Hierarchy (TPH):
In TPH, a single table is used to store all entities in the inheritance hierarchy, and a discriminator column is used to distinguish between the different types.public class Animal { public int Id { get; set; } public string Name { get; set; } } public class Dog : Animal { public string Breed { get; set; } } public class Cat : Animal { public string Color { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Animal>() .HasDiscriminator<string>("AnimalType") .HasValue<Dog>("Dog") .HasValue<Cat>("Cat"); }
- Use case: Use TPH when you want to store all classes in the hierarchy in a single table.
-
Table per Type (TPT):
In TPT, each class in the inheritance hierarchy has its own table, but these tables are linked through foreign keys.protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Dog>() .ToTable("Dogs") .HasBaseType<Animal>(); modelBuilder.Entity<Cat>() .ToTable("Cats") .HasBaseType<Animal>(); }
- Use case: Use TPT when you need to keep separate tables for each type, but need to join them together when querying.
-
Table per Concrete Class (TPC):
In TPC, each class in the hierarchy has its own table and there is no shared base table. This means the data for each class is duplicated across tables.protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Dog>().ToTable("Dogs"); modelBuilder.Entity<Cat>().ToTable("Cats"); }
- Use case: Use TPC when you need fully separated tables for each type without shared properties between classes.
54. How do you handle soft deletes in EF Core?
Question:
How do you implement soft deletes in EF Core, where a record is marked as deleted but not actually removed from the database?
Answer:
In EF Core, you can handle soft deletes by adding a flag (e.g., IsDeleted
) to your entities and filtering out soft-deleted records globally using a query filter.
-
Step 1: Add an
IsDeleted
property to your entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public bool IsDeleted { get; set; } }
-
Step 2: Configure the global filter in
OnModelCreating
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .HasQueryFilter(p => !p.IsDeleted); // Global query filter for soft deletes }
-
Step 3: Soft delete operation:
When performing a soft delete, you just update the
IsDeleted
flag:var product = dbContext.Products.Find(1); if (product != null) { product.IsDeleted = true; dbContext.SaveChanges(); }
-
Step 4: Handling includes with soft deletes:
If you want to include soft-deleted records in a query, you can override the global filter:
var productsIncludingDeleted = dbContext.Products .IgnoreQueryFilters() // This disables the global filter temporarily .ToList();
- Use case: Soft deletes are useful when you want to keep historical data for auditing or recovery purposes.
55. How do you manage database schema versioning with EF Core migrations?
Question:
How do you manage database schema versioning using EF Core migrations?
Answer:
EF Core provides a migration system that allows you to track changes to your database schema over time. It’s essential for keeping the database schema in sync with the application code.
-
Add a migration:
Whenever you make changes to your model (e.g., adding, removing, or modifying entities), you can generate a migration using thedotnet ef migrations add
command.dotnet ef migrations add AddProductTable
-
Apply migrations:
To update the database schema, use thedotnet ef database update
command.dotnet ef database update
-
Versioning:
Each migration file is timestamped and can be used to roll back to a specific state of the database schema. EF Core maintains a special table,__EFMigrationsHistory
, in the database to track applied migrations. -
Roll back a migration:
You can remove the last migration if it has not yet been applied to the database using:dotnet ef migrations remove
To revert the database to a previous state, you can use:
dotnet ef database update <MigrationName>
- Use case: Migrations are essential in team environments to maintain synchronization between the application's database schema and the codebase.
56. How do you handle database seeding in EF Core?
Question:
How do you implement database seeding in EF Core to add initial data to your database?
Answer:
EF Core provides a way to seed data using the HasData()
method in the OnModelCreating
method, which allows you to populate the database with initial data when migrations are applied.
-
Example: Seeding data using
HasData()
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>().HasData( new Product { ProductId = 1, Name = "Product1", Price = 10.0m }, new Product { ProductId = 2, Name = "Product2", Price = 20.0m } ); }
-
Running migrations to apply seed data: After defining your seed data, run the migration and update the database:
dotnet ef migrations add SeedProducts dotnet ef database update
-
Use Case:
Database seeding is useful for adding initial lookup data (like categories, roles, or
default settings) when setting up the application for the first time.
Here are more advanced EF Core technical coding interview questions for experienced developers:
57. How do you handle concurrency in EF Core?
Question:
How do you handle concurrency issues in EF Core when multiple users or processes try to update the same record simultaneously?
Answer:
EF Core provides two approaches to handle concurrency:
-
Optimistic Concurrency Control:
- Optimistic concurrency allows multiple users to retrieve the same record but requires a way to detect conflicts when saving changes. EF Core uses a timestamp or version number field to track the state of an entity.
Steps to implement Optimistic Concurrency:
-
Add a
Timestamp
orRowVersion
property to the entity:public class Product { public int ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } [Timestamp] public byte[] RowVersion { get; set; } // This field is used for concurrency }
-
Enable concurrency control in
OnModelCreating
:protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property(p => p.RowVersion) .IsRowVersion(); }
-
When saving changes, EF Core automatically checks if the
RowVersion
has changed in the database. If it has, aDbUpdateConcurrencyException
is thrown. -
Handling the exception:
try { dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { // Handle concurrency conflict }
-
Use case: Optimistic concurrency is ideal for scenarios where conflicts are rare and you want to minimize locking, like in web applications.
-
Pessimistic Concurrency Control:
- Pessimistic concurrency locks the record so that other users cannot modify it until the transaction is completed. EF Core does not have built-in support for pessimistic locking, but it can be achieved through raw SQL queries.
var product = dbContext.Products .FromSqlRaw("SELECT * FROM Products WHERE ProductId = {0} FOR UPDATE", productId) .FirstOrDefault();
- Use case: Pessimistic concurrency is useful in scenarios where data consistency is critical, such as financial applications.
58. What are shadow properties and how are they used in EF Core?
Question:
What are shadow properties, and how are they used in EF Core?
Answer:
Shadow properties are properties that are defined in the database but are not directly included in the entity class. These properties are not part of the entity's class but are still tracked by EF Core.
-
Defining Shadow Properties: Shadow properties can be added using the
ModelBuilder
in theOnModelCreating
method.public class Product { public int ProductId { get; set; } public string Name { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .Property<DateTime>("CreatedDate"); // Shadow property }
-
Accessing Shadow Properties: Shadow properties are not part of the entity class, so they are accessed via the
Entry()
method.var createdDate = dbContext.Entry(product).Property("CreatedDate").CurrentValue;
-
Use case: Shadow properties are useful for auditing, versioning, or storing metadata without modifying the entity class.
59. How do you configure a Many-to-Many relationship in EF Core?
Question:
How do you configure a many-to-many relationship between two entities in EF Core?
Answer:
In EF Core 5 and later, you can configure a many-to-many relationship without explicitly creating a join entity.
-
Example: Many-to-Many Relationship:
Let's say we have
Student
andCourse
entities, where a student can enroll in multiple courses, and a course can have multiple students.public class Student { public int StudentId { get; set; } public string Name { get; set; } public ICollection<Course> Courses { get; set; } } public class Course { public int CourseId { get; set; } public string Title { get; set; } public ICollection<Student> Students { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>() .HasMany(s => s.Courses) .WithMany(c => c.Students) .UsingEntity<Dictionary<string, object>>( "StudentCourse", // Join table name j => j.HasOne<Course>().WithMany().HasForeignKey("CourseId"), j => j.HasOne<Student>().WithMany().HasForeignKey("StudentId")); }
-
Use case: This approach works well when there are no additional properties on the join table. If additional properties (e.g.,
EnrollmentDate
) are needed, you can create an explicit join entity.
60. What is the difference between AsNoTracking()
and AsTracking()
in EF Core?
Question:
What is the difference between AsNoTracking()
and AsTracking()
in EF Core, and when would you use each?
Answer:
-
AsNoTracking()
:- This method returns a query where the entities are not tracked by the
DbContext
. This improves performance because no change tracking is done. - Use case: Use
AsNoTracking()
when you are reading data for display purposes (e.g., read-only operations) and do not need to update the data.
var products = dbContext.Products.AsNoTracking().ToList();
- This method returns a query where the entities are not tracked by the
-
AsTracking()
:- This is the default behavior of EF Core, where entities are tracked by the
DbContext
. This means that EF Core will track any changes to the entities and save them to the database whenSaveChanges()
is called. - Use case: Use
AsTracking()
when you need to modify the retrieved entities and save those changes back to the database.
var product = dbContext.Products.AsTracking().FirstOrDefault();
- This is the default behavior of EF Core, where entities are tracked by the
-
Performance Considerations:
AsNoTracking()
is typically more performant for read-only queries, as it reduces the overhead of change tracking.AsTracking()
should be used when you need to perform operations like update or delete on the entities.
61. How do you configure a unique constraint in EF Core?
Question:
How do you configure a unique constraint for an entity property in EF Core?
Answer:
In EF Core, you can configure a unique constraint using the HasIndex()
method and then apply the IsUnique()
modifier.
-
Example: Configuring a Unique Constraint:
public class Product { public int ProductId { get; set; } public string Name { get; set; } public string SKU { get; set; } // SKU must be unique } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>() .HasIndex(p => p.SKU) .IsUnique(); // Adding a unique constraint on the SKU property }
-
Use case: This is useful when you need to enforce a unique constraint on a property (e.g.,
Email
,SKU
,Username
).
62. How do you handle database migrations in a team environment with EF Core?
Question:
How do you handle database migrations in a team environment with EF Core?
Answer:
In a team environment, it's important to have a strategy to keep the database schema synchronized among all developers. Here are some best practices:
-
Consistent Migration Management:
- Ensure that every developer adds and applies migrations before starting new work.
- Always run
dotnet ef migrations add
anddotnet ef database update
to ensure migrations are up-to-date.
-
Migration Workflow:
- Developers should add migrations in sequence (one at a time) to avoid conflicts. Each developer should pull the latest changes from the version control system before adding their own migration.
-
Apply Migrations to Local Development Database:
- Always apply migrations to local databases and validate them before committing changes.
-
Review Migrations:
- Migrations should be reviewed to ensure that they do not contain unnecessary changes (e.g., changes that alter or drop existing data, unless intended).
-
Migration in CI/CD:
- Set up Continuous Integration (CI) pipelines that automatically apply migrations to test/staging databases to ensure that all migrations are applied correctly.
63. What is Include()
and ThenInclude()
in EF Core, and how do they work with relationships?
Question:
Explain the purpose of Include()
and ThenInclude()
in EF Core. How do they work in loading related data?
Answer:
In EF Core, Include()
is used for eager loading of related data, while ThenInclude()
is used for including nested related data.
-
Include()
:Include()
is used to load related data along with the primary data.
var orders = dbContext.Orders.Include(o => o.OrderItems).ToList();
-
ThenInclude()
:ThenInclude()
is used to load additional related data from the related entities fetched viaInclude()
.
var orders = dbContext.Orders .Include(o => o.OrderItems) .ThenInclude(oi => oi.Product) // Include related Product for each OrderItem .ToList();
Use case: Use Include()
when you need to eagerly load direct relationships, and ThenInclude()
when you need to load deeper nested relationships.
64. How do you use raw SQL queries in EF Core?
Question:
How do you execute raw SQL queries in EF Core, and when would you use them?
Answer:
EF Core allows executing raw SQL queries to interact directly with the database. This can be useful when you need complex queries that cannot be easily expressed using LINQ or need to improve performance.
1. Raw SQL with FromSqlRaw():
- Use FromSqlRaw() to execute SELECT queries.
Example:
var products = dbContext.Products
.FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 20)
.ToList();
2. Executing non-SELECT queries:
- Use ExecuteSqlRaw() for executing non-SELECT queries (e.g., INSERT, UPDATE, DELETE).
Example:
dbContext.Database.ExecuteSqlRaw("UPDATE Products SET Price = Price * 1.1 WHERE CategoryId = {0}", categoryId);
3. Parameterized Queries:
- Always use parameterized queries to prevent SQL injection vulnerabilities.
Example:
var productId = 1;
var product = dbContext.Products
.FromSqlRaw("SELECT * FROM Products WHERE ProductId = {0}", productId)
.FirstOrDefault();
4. Use case: Raw SQL queries are often used when you need to execute complex queries, work with stored procedures, or improve performance for certain scenarios that cannot be handled easily with LINQ.
65. What are the differences between Add(), Update(), and Attach() in EF Core?
Question:
What is the difference between Add(), Update(), and Attach() methods in EF Core?
Answer:
In EF Core, these methods are used to manage the state of entities in the DbContext.
1. Add():
- Adds a new entity to the context. The entity is tracked as Added and will be inserted into the database when SaveChanges() is called.
- Use case: When you are inserting a new entity.
Example:
var newProduct = new Product { Name = "New Product" };
dbContext.Products.Add(newProduct);
dbContext.SaveChanges();
2. Update():
- Marks an existing entity as Modified. This method is typically used when you want to modify an entity that already exists in the database.
- Use case: When you want to update an existing entity's values, and EF Core will track it for changes.
Example:
var product = dbContext.Products.Find(1);
product.Name = "Updated Product";
dbContext.Products.Update(product);
dbContext.SaveChanges();
3. Attach():
- Attaches an entity to the context without marking it as Added or Modified. The entity is tracked as Unchanged unless its properties are modified.
- Use case: When you have an entity that exists in the database, but you want to attach it to the context without modifying it. Useful when you only want to track an entity without triggering any insert or update.
Example:
var product = new Product { ProductId = 1 };
dbContext.Products.Attach(product); // No change to entity, just tracking
dbContext.SaveChanges();
66. How do you optimize database performance with EF Core?
Question:
What techniques do you use to optimize database performance in EF Core?
Answer:
There are several ways to optimize performance in EF Core:
1. Use AsNoTracking() for Read-Only Queries:
- When you don’t need to update the entities, use AsNoTracking() to avoid the overhead of change tracking.
Example:
var products = dbContext.Products.AsNoTracking().ToList();
2. Lazy Loading vs Eager Loading:
- Eager Loading: Use Include() to load related data in a single query to reduce the number of round trips to the database.
Example:
var order = dbContext.Orders.Include(o => o.OrderItems).FirstOrDefault();
- Lazy Loading: Be cautious when using lazy loading because it can result in multiple queries being sent to the database. Enable lazy loading only when necessary and control it carefully.
3. Use Select() for Optimized Queries:
- Only select the necessary columns to improve performance and reduce the amount of data being fetched.
Example:
var products = dbContext.Products
.Where(p => p.Price > 100)
.Select(p => new { p.Name, p.Price })
.ToList();
4. Batch Updates:
- Instead of performing multiple SaveChanges() calls, try to batch them together to reduce database round-trips.
5. Indexing:
- Ensure that the database tables have appropriate indexes on columns that are frequently queried. EF Core can configure indexes using HasIndex().
Example:
modelBuilder.Entity<Product>()
.HasIndex(p => p.Name)
.IsUnique();
6. Avoid N+1 Query Problem:
- Avoid executing additional queries in loops. Instead, use Include() to eagerly load related entities or Select() to retrieve only necessary data.
7. Use Raw SQL for Complex Queries:
- If a query is too complex for LINQ or causes performance problems, use raw SQL for fine-grained control over query performance.
67. What are value converters in EF Core?
Question:
What are value converters in EF Core, and how do they help in customizing how values are stored in the database?
Answer:
Value converters in EF Core allow you to define how data is converted when being read from and written to the database. They are used when you need to store data in a different format in the database than it is in your entity.
1. Example: If you want to store a DateTime as a string in the database (in a specific format), you can use a value converter.
Example:
public class Product
{
public int ProductId { get; set; }
public DateTime CreatedDate { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var dateTimeConverter = new ValueConverter<DateTime, string>(
v => v.ToString("yyyy-MM-dd"), // Convert DateTime to string
v => DateTime.Parse(v)); // Convert string to DateTime
modelBuilder.Entity<Product>()
.Property(p => p.CreatedDate)
.HasConversion(dateTimeConverter);
}
2. Use case: Value converters are useful when you need to store complex data types in a simplified format (e.g., converting enum values, DateTime values, or custom objects to a database column).
68. How do you handle schema changes during runtime with EF Core?
Question:
How do you manage schema changes during runtime without downtime in EF Core?
Answer:
In EF Core, schema changes typically require migrations, but handling runtime schema changes without downtime can be more complex, especially in production environments. Here are some strategies to handle schema changes during runtime:
1. Zero-Downtime Migrations:
- Break down migrations into smaller steps to avoid downtime. For example:
- Add new columns with default values first.
- Apply any business logic changes after the column is added.
- Once the changes are applied, you can remove old columns or tables if necessary.
2. Rolling Migrations:
- Apply migrations in a rolling manner, updating one node at a time in a multi-node application.
- Use application versioning to ensure backward compatibility during the migration.
3. Use Database Views:
- Use database views to abstract complex schema changes and make them transparent to the application.
4. Feature Toggles for Schema Changes:
- Use feature toggles to gradually roll out new features and schema changes without breaking the application.
5. Use Blue-Green Deployment:
- Use blue-green deployment strategies where you have two production environments (Blue and Green). You deploy the new schema to one environment and switch traffic over once everything is validated.
69. How do you perform database migrations in a cloud-based environment like Azure?
Question:
How do you perform EF Core database migrations in a cloud-based environment such as Azure?
Answer:
To perform EF Core migrations in a cloud-based environment (e.g., Azure), the process is similar to how you would perform migrations locally, but with considerations for cloud infrastructure and deployment pipelines.
1. Using Azure CLI:
- You can run migrations directly from Azure via the Azure CLI by connecting to the app's web app and using the dotnet ef commands.
bash
az webapp ssh --name <app_name> --resource-group <resource_group>
dotnet ef migrations add MigrationName
dotnet ef database update
2. Using CI/CD Pipelines:
- Implement CI/CD pipelines (e.g., Azure DevOps, GitHub Actions) to automatically run migrations during the deployment process.
- You can add migration steps to your pipeline configuration:
yaml
steps:
- task: DotNetCoreCLI@2
displayName: 'dotnet ef database update'
inputs:
command: 'custom'
custom: 'ef'
arguments: 'database update'
3. Ensure Backward Compatibility:
- As with any production migration, ensure that the schema changes are backward-compatible with the previous version of the application to avoid breaking changes.
Comments
Post a Comment