Exploring ADO.NET and Entity Framework
Exploring ADO.NET and Entity Framework Interview with follow-up questions
1. What is ADO.NET and Entity Framework in .NET Framework?
ADO.NET is the low-level data access layer in .NET. It provides classes for connecting to databases, executing SQL commands, and reading results. It uses a disconnected model: data is fetched into DataSet/DataTable objects in memory, manipulated, and optionally written back to the database.
Core ADO.NET classes:
SqlConnection– Manages a database connection.SqlCommand– Executes SQL queries or stored procedures.SqlDataReader– Fast, forward-only, read-only stream of results.SqlDataAdapter– Bridges between aDataSetand a database.DataSet/DataTable– In-memory representation of relational data.
ADO.NET supports multiple providers: SqlClient (SQL Server), Npgsql (PostgreSQL), MySqlConnector, etc.
Entity Framework Core (EF Core) is Microsoft's modern Object-Relational Mapper (ORM) built on top of ADO.NET. Rather than writing SQL manually, you work with .NET objects (entities) and let EF Core translate LINQ queries into SQL.
Current version: EF Core 8/9, which adds:
- Complex types: Value objects without a separate table/key.
- Raw SQL improvements:
SqlQuery()for arbitrary SQL returning entity types. - Bulk operations:
ExecuteUpdateAsync()andExecuteDeleteAsync()for set-based updates/deletes without loading entities. - JSON columns: Map JSON data stored in a column to .NET types.
- Primitive collections: Store
List,Listetc. in JSON columns.
When to use each:
- Use EF Core for most application development — productivity, safety, and maintainability.
- Use ADO.NET (or Dapper) for maximum performance, complex raw SQL, or stored procedure-heavy scenarios.
Follow-up 1
What are the key features of ADO.NET?
Some of the key features of ADO.NET include:
- Disconnected data access model: ADO.NET allows data to be retrieved from a data source and then disconnected from it, enabling efficient data manipulation and reducing the load on the database server.
- Support for multiple data sources: ADO.NET provides a consistent programming model for accessing data from different types of data sources, including databases, XML files, and web services.
- Data provider model: ADO.NET uses a data provider model, where each data source has its own specific data provider. This allows developers to work with different data sources using a unified API.
- Support for data binding: ADO.NET provides support for data binding, allowing developers to easily bind data to user interface controls.
- Support for transactions: ADO.NET supports transactions, which allow multiple database operations to be grouped together and treated as a single unit of work.
Follow-up 2
What are the key features of Entity Framework?
Some of the key features of Entity Framework include:
- Object-relational mapping (ORM): Entity Framework enables developers to work with data in the form of objects and classes, and automatically maps these objects to database tables and columns.
- LINQ support: Entity Framework provides support for Language-Integrated Query (LINQ), allowing developers to write queries against the data using a strongly-typed query syntax.
- Change tracking: Entity Framework tracks changes made to the objects and automatically generates the necessary SQL statements to persist those changes to the database.
- Lazy loading: Entity Framework supports lazy loading, which means that related objects are loaded from the database only when they are accessed, improving performance.
- Code-first development: Entity Framework allows developers to define the database schema using code-first approach, where the database is generated based on the classes and relationships defined in code.
Follow-up 3
How do they differ from each other?
ADO.NET and Entity Framework differ in the way they approach data access and manipulation. ADO.NET is a lower-level data access technology that provides a more direct and fine-grained control over the database operations. It uses a disconnected data access model, where data is retrieved from the database, modified, and then saved back to the database. On the other hand, Entity Framework is an ORM framework that abstracts away the low-level database operations and allows developers to work with data in the form of objects and classes. It automatically generates the necessary SQL statements to perform database operations based on the object model. Entity Framework also provides additional features such as change tracking, lazy loading, and LINQ support, which are not available in ADO.NET.
Follow-up 4
Can you provide a practical example where you would use ADO.NET over Entity Framework and vice versa?
Sure! Here's an example where you might choose to use ADO.NET over Entity Framework:
Let's say you have a performance-critical application that needs to perform complex database operations involving large amounts of data. In this case, using ADO.NET would give you more control over the database operations and allow you to optimize the performance by writing custom SQL queries and fine-tuning the data access code.
On the other hand, Entity Framework would be a better choice in scenarios where you prioritize productivity and ease of development over performance. For example, if you are building a small to medium-sized application with a simple data model and you want to quickly generate the database schema and perform CRUD operations, Entity Framework's code-first approach and automatic SQL generation capabilities would save you a lot of time and effort.
2. How does ADO.NET handle data access operations?
ADO.NET uses a disconnected architecture — data is retrieved from the database, worked with in memory, and changes are saved back when needed. This differs from always-open cursor models.
Core workflow:
// 1. Open a connection
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
// 2. Create and execute a command
await using var command = new SqlCommand("SELECT Id, Name FROM Products WHERE Price > @price", connection);
command.Parameters.AddWithValue("@price", 10.00m);
// 3. Read results with DataReader (fast, forward-only)
await using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var id = reader.GetInt32(0);
var name = reader.GetString(1);
}
Key components:
- Connection (
SqlConnection): Represents a physical database connection. Always useusing/await usingto ensure it is closed and returned to the connection pool. - Command (
SqlCommand): Executes SQL statements or stored procedures. Always use parameterized queries (never string concatenation) to prevent SQL injection. - DataReader (
SqlDataReader): High-performance, forward-only, read-only cursor — best for reading large result sets efficiently. - DataAdapter + DataSet: Fills a disconnected
DataSetin memory; useful for legacy scenarios or offline data manipulation, but less common in modern code. - Connection pooling: ADO.NET automatically pools connections, so
Open()/Close()is fast. Pool size is configurable.
Modern usage note: In new applications, ADO.NET is often accessed indirectly through EF Core or micro-ORMs like Dapper, but understanding it is essential for diagnosing performance and for raw SQL scenarios.
Follow-up 1
What are the key components of ADO.NET?
The key components of ADO.NET are:
Connection: Represents a connection to a data source. It provides methods for establishing and managing the connection.
Command: Represents a SQL statement or stored procedure to be executed against a data source. It provides methods for executing the command and retrieving the results.
DataReader: Provides a forward-only, read-only stream of data from a data source. It is used for retrieving large amounts of data quickly and efficiently.
DataSet: Represents an in-memory cache of data that can be used independently of a data source. It can hold multiple tables, relationships, and constraints.
DataAdapter: Acts as a bridge between a DataSet and a data source. It is used to fill a DataSet with data from the data source and update the data source with changes made in the DataSet.
DataCommandBuilder: Automatically generates SQL commands for updating a data source based on changes made in a DataSet.
Follow-up 2
How does it manage connection with the database?
ADO.NET manages the connection with the database through the Connection class. To establish a connection, you need to create an instance of the Connection class and set its ConnectionString property to specify the connection details such as the database server, username, password, etc. Once the connection is established, you can use the Connection object to execute commands against the database and retrieve data.
Here's an example of how to manage a connection with a SQL Server database using ADO.NET:
using System.Data.SqlClient;
string connectionString = "Data Source=myServerAddress;Initial Catalog=myDatabase;User ID=myUsername;Password=myPassword;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
// Execute commands or retrieve data
}
Follow-up 3
What is a DataSet in ADO.NET?
A DataSet in ADO.NET is an in-memory cache of data that can hold multiple tables, relationships, and constraints. It is a disconnected representation of data that can be used independently of a data source. A DataSet can be populated with data from a data source using a DataAdapter, and it can be manipulated and queried using LINQ or other data manipulation techniques.
Here's an example of how to create and populate a DataSet with data from a SQL Server database using ADO.NET:
using System.Data;
using System.Data.SqlClient;
string connectionString = "Data Source=myServerAddress;Initial Catalog=myDatabase;User ID=myUsername;Password=myPassword;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Customers", connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "Customers");
// Use the dataSet to work with the data
}
Follow-up 4
What is a DataReader in ADO.NET?
A DataReader in ADO.NET provides a forward-only, read-only stream of data from a data source. It is used for retrieving large amounts of data quickly and efficiently, especially when you only need to read the data once and don't need to update it. The DataReader is optimized for performance and memory usage, as it retrieves data in a sequential manner and doesn't store the entire result set in memory.
Here's an example of how to use a DataReader to retrieve data from a SQL Server database using ADO.NET:
using System.Data.SqlClient;
string connectionString = "Data Source=myServerAddress;Initial Catalog=myDatabase;User ID=myUsername;Password=myPassword;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand("SELECT * FROM Customers", connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
// Read data from the reader
}
}
3. What is the role of Entity Framework in .NET?
Entity Framework Core (EF Core) is Microsoft's official Object-Relational Mapper (ORM) for .NET. It sits on top of ADO.NET and allows developers to work with databases using strongly-typed .NET objects instead of writing raw SQL.
Core role:
- Maps .NET classes to database tables (entities) and class properties to columns.
- Translates LINQ queries into database-specific SQL at runtime.
- Tracks changes to entities and generates the correct INSERT/UPDATE/DELETE SQL when
SaveChanges()is called. - Manages database schema evolution via migrations — code-first changes to the model generate migration scripts.
Example:
// Define entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
// Query via LINQ
var cheapProducts = await context.Products
.Where(p => p.Price < 20)
.OrderBy(p => p.Name)
.ToListAsync();
// Bulk update without loading entities (EF Core 7+)
await context.Products
.Where(p => p.Price < 1)
.ExecuteUpdateAsync(s => s.SetProperty(p => p.Price, 1));
EF Core 8/9 highlights:
- Complex types: Map value objects to owned columns without a separate key.
- Primitive collections: Store
Listas a JSON column. - JSON columns: Map JSON stored in a column to .NET types with LINQ query support.
- ExecuteUpdate / ExecuteDelete: Efficient bulk operations at the database level.
- Raw SQL enhancements:
FromSql()andSqlQuery()for typed raw SQL queries.
EF Core supports SQL Server, PostgreSQL (Npgsql), MySQL, SQLite, Cosmos DB, and more via provider packages.
Follow-up 1
What are the key components of Entity Framework?
The key components of Entity Framework are:
Entity Data Model (EDM): It represents the conceptual model of the database and defines the entities, relationships, and mappings between them.
Object Services: It provides the API for querying, inserting, updating, and deleting entities in the database.
Entity SQL: It is a query language similar to SQL but operates on the conceptual model defined by the EDM.
LINQ to Entities: It allows developers to write queries against the conceptual model using LINQ (Language Integrated Query).
Entity Client: It is the ADO.NET provider for Entity Framework and provides the necessary infrastructure for connecting to the database and executing queries.
Follow-up 2
How does it manage connection with the database?
Entity Framework manages the connection with the database using the Entity Connection string. The connection string contains the necessary information to establish a connection, such as the server name, database name, authentication details, and other options.
When an application interacts with the database using Entity Framework, it creates an instance of the DbContext class, which represents a session with the database. The DbContext class internally manages the connection and provides methods for querying, inserting, updating, and deleting entities.
Follow-up 3
What is a DbContext in Entity Framework?
In Entity Framework, the DbContext class is the main class that represents a session with the database. It is responsible for managing the connection, tracking changes to entities, and executing queries against the database.
The DbContext class is a part of the Entity Framework Core API and provides a high-level API for working with entities. It exposes methods for querying, inserting, updating, and deleting entities, as well as configuring the mapping between entities and database tables.
Here is an example of creating a DbContext class:
public class MyDbContext : DbContext
{
public DbSet Customers { get; set; }
public DbSet Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("connectionString");
}
}
Follow-up 4
What is a DbSet in Entity Framework?
In Entity Framework, a DbSet represents a collection of entities of a specific type in the context. It is a property of the DbContext class and provides a way to query, insert, update, and delete entities of that type.
A DbSet is used to perform CRUD (Create, Read, Update, Delete) operations on entities. It allows developers to query the database using LINQ or Entity SQL, and also provides methods for adding, updating, and deleting entities.
Here is an example of using a DbSet to query entities:
using (var context = new MyDbContext())
{
var customers = context.Customers.Where(c => c.City == "New York").ToList();
// Perform operations on the customers
}
4. What are the different approaches in Entity Framework?
EF Core supports two primary workflow approaches (the legacy "Model First" from classic EF is not supported in EF Core):
1. Code First (recommended for new projects)
You define your domain model as C# classes, and EF Core generates the database schema from those classes using migrations.
public class Order
{
public int Id { get; set; }
public DateTime PlacedAt { get; set; }
public List Lines { get; set; } = [];
}
Workflow: Define entities → Configure with Fluent API or Data Annotations → dotnet ef migrations add → dotnet ef database update.
Benefits: The model is the source of truth; schema is version-controlled alongside code; clean domain-first design.
2. Database First
You start from an existing database and use EF Core tooling to scaffold entity classes and a DbContext from the database schema.
dotnet ef dbcontext scaffold "ConnectionString" Microsoft.EntityFrameworkCore.SqlServer \
--output-dir Models
Benefits: Useful when the database already exists or is managed by a DBA team; keeps the DB as the source of truth.
Drawbacks: Regenerating after schema changes can overwrite customizations; requires careful management of partial classes.
3. Model First (classic EF only — not in EF Core)
Model First (designing a visual EDMX model then generating both code and database from it) was available in classic Entity Framework but is not supported in EF Core. EF Core dropped the EDMX designer in favor of Code First.
Choosing an approach:
- New greenfield project: Code First.
- Existing database: Database First scaffolding, then switch to Code First migrations for ongoing changes.
Follow-up 1
What is Code First approach?
Code First approach is a development workflow in Entity Framework where you start with writing classes (entities) and then Entity Framework generates the database schema based on those classes. It allows you to define the database structure using code and provides flexibility in mapping entities to database tables and columns.
Follow-up 2
What is Database First approach?
Database First approach is a development workflow in Entity Framework where you start with an existing database and then Entity Framework generates the entity classes (code) based on the database schema. It allows you to generate entity classes from an existing database and provides a quick way to get started with Entity Framework.
Follow-up 3
What is Model First approach?
Model First approach is a development workflow in Entity Framework where you start with designing the conceptual model (entities) using Entity Framework Designer or EDMX file, and then Entity Framework generates the database schema based on the model. It allows you to visually design the database model and provides a graphical interface to define entities, relationships, and mappings.
Follow-up 4
Can you provide a practical example for each approach?
Sure! Here are practical examples for each approach:
- Code First approach example:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ApplicationDbContext : DbContext
{
public DbSet Products { get; set; }
}
- Database First approach example:
Assuming you have an existing database with a table named 'Products', Entity Framework can generate the following entity class for you:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
- Model First approach example:
Using Entity Framework Designer or EDMX file, you can visually design the conceptual model and Entity Framework will generate the database schema for you based on the model.
5. What is the concept of Lazy Loading in Entity Framework?
Lazy loading is a pattern where related entities are not loaded from the database until they are accessed for the first time. EF Core supports lazy loading, but it is disabled by default and must be explicitly opted into.
How it works:
When you access a navigation property (e.g., order.Customer), EF Core automatically issues an additional SQL query to load the related data at that moment — if lazy loading is enabled.
Enabling lazy loading in EF Core:
Install the proxy package and enable it:
// Package: Microsoft.EntityFrameworkCore.Proxies
builder.Services.AddDbContext(options =>
options.UseSqlServer(connectionString)
.UseLazyLoadingProxies());
Navigation properties must be virtual for proxy-based lazy loading.
The N+1 problem — a critical caveat:
Lazy loading can silently cause N+1 query problems: loading 100 orders, then accessing order.Customer on each one fires 100 separate SQL queries. This severely impacts performance.
Alternatives (preferred in most cases):
- Eager loading (
Include): Load related data upfront in a single query.csharp var orders = await context.Orders.Include(o => o.Customer).ToListAsync(); - Explicit loading: Load a specific navigation property on demand.
csharp await context.Entry(order).Reference(o => o.Customer).LoadAsync(); - Projection (
Select): Query only the fields you need — most efficient.csharp var result = await context.Orders .Select(o => new { o.Id, CustomerName = o.Customer.Name }) .ToListAsync();
Best practice: Prefer eager loading or projection for known access patterns. Reserve lazy loading for specific exploratory scenarios, and always profile queries.
Follow-up 1
How does Lazy Loading work?
Lazy Loading works by creating proxy objects for the related entities. When a navigation property is accessed, the proxy object intercepts the access and triggers a database query to load the related entities. The loaded entities are then cached for future access.
Follow-up 2
What are the advantages and disadvantages of Lazy Loading?
Advantages of Lazy Loading:
- Reduced memory usage: Only the entities that are actually needed are loaded, which can save memory.
- Improved performance: Loading related entities on-demand can improve performance by reducing the number of database queries.
Disadvantages of Lazy Loading:
- Increased complexity: Lazy Loading introduces additional complexity to the code, as developers need to be aware of when and how related entities are loaded.
- Potential performance issues: If lazy loading is not used carefully, it can lead to performance issues such as the N+1 problem, where multiple database queries are executed to load related entities.
- Difficulty in managing disconnected entities: Lazy Loading can cause issues when working with disconnected entities, as related entities may not be available when needed.
Follow-up 3
How can you enable or disable Lazy Loading?
In Entity Framework, Lazy Loading can be enabled or disabled at the context level or at the individual navigation property level.
To enable or disable Lazy Loading at the context level, you can use the Configuration.LazyLoadingEnabled property:
// Enable Lazy Loading
context.Configuration.LazyLoadingEnabled = true;
// Disable Lazy Loading
context.Configuration.LazyLoadingEnabled = false;
To enable or disable Lazy Loading at the individual navigation property level, you can use the virtual keyword in the entity class:
public class Order
{
public int OrderId { get; set; }
public virtual Customer Customer { get; set; } // Lazy Loading enabled
public Customer Customer { get; set; } // Lazy Loading disabled
}
By default, Lazy Loading is enabled in Entity Framework.
Follow-up 4
Can you provide a practical scenario where Lazy Loading would be beneficial?
Lazy Loading can be beneficial in scenarios where you have a large number of related entities and you want to minimize the initial loading time and memory usage.
For example, consider an e-commerce application where you have an Order entity that is related to a Customer entity, which in turn is related to an Address entity. Without Lazy Loading, when you load an Order, you would also need to load the associated Customer and Address entities upfront, even if you don't need them immediately. This can result in unnecessary database queries and increased memory usage.
With Lazy Loading enabled, the Customer and Address entities would only be loaded when they are accessed for the first time, reducing the initial loading time and memory usage. This can be particularly useful in scenarios where you frequently work with the Order entity without needing to access its related entities.
Live mock interview
Mock interview: Exploring ADO.NET and Entity Framework
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.