.NET ProgrammingUnit 610 min read
ADO.NET: Database Access in .NET with Connections, Commands, and Data Adapters
Unit 6 of .NET Programming covers ADO.NET fundamentals—how to connect to databases, execute queries, handle data with DataReaders and DataAdapters, and manage transactions—with real-world examples from Nepalese apps like eSewa and Ncell, plus step-by-step code traces.
TAKEAWAYS:
- ADO.NET uses Connection, Command, DataReader, and DataAdapter objects to interact with databases like SQL Server or MySQL.
- Disconnected architecture (DataSet/DataTable) is ideal for offline processing, while connected (DataReader) is faster for read-heavy apps.
- Transactions ensure atomicity in multi-step operations (e.g., bank transfers).
- Parameterized queries prevent SQL injection—critical for security in apps like eSewa.
- Stored procedures improve performance and reduce network traffic.
- LINQ to SQL bridges C# and SQL for type-safe queries.
---
### Core Concepts: ADO.NET Architecture
ADO.NET is Microsoft’s data access technology for .NET apps. It provides a **disconnected** model (DataSet/DataTable) and a **connected** model (DataReader) to interact with databases. The key components are:
```figure
{"type":"network","nodes":["Connection","Command","DataReader","DataAdapter","DataSet"],"edges":[["Connection","Command","Creates"],["Command","DataReader","Returns"],["Command","DataAdapter","Used by"],["DataAdapter","DataSet","Fills/Updates"]],"directed":true,"caption":"ADO.NET component interactions (disconnected model: DataSet/DataTable; connected model: DataReader)"}
1. Connection Object
- Represents a live connection to a database (SQL Server, MySQL, Oracle).
- Key properties:
ConnectionString: Contains server, database, credentials (e.g.,"Server=localhost;Database=Northwind;User Id=sa;Password=123").State: Tracks if the connection is open/closed.
- Example:
SqlConnection conn = new SqlConnection("Server=localhost;Database=TestDB;Integrated Security=True;"); conn.Open(); // Opens the connection
2. Command Object
- Executes SQL commands or stored procedures.
- Methods:
ExecuteNonQuery(): ForINSERT,UPDATE,DELETE(returns row count).ExecuteReader(): ForSELECT(returns aDataReader).ExecuteScalar(): For single-value queries (e.g.,COUNT(*)).
- Parameterized Queries (prevent SQL injection):
SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE Username=@user", conn); cmd.Parameters.AddWithValue("@user", "john_doe");
In the Real World
eSewa (Nepal)
- Idea Used: Transactions and Parameterized Queries
- How: When you pay a bill, eSewa deducts money from your account atomically (either both deduction and credit succeed, or neither does). It uses
SqlTransactionto group multiple SQL commands into a single unit. - Example:
BEGIN TRANSACTION UPDATE Accounts SET Balance = Balance - 1000 WHERE UserId = 1; UPDATE MerchantAccounts SET Balance = Balance + 1000 WHERE MerchantId = 5; COMMIT;
Ncell Recharge System
- Idea Used: Stored Procedures and DataReader
- How: Ncell’s backend uses stored procedures to validate recharge requests quickly. A
DataReaderstreams recharge data (e.g., customer ID, amount) without loading entire tables into memory. - Example Stored Procedure:
CREATE PROCEDURE ValidateRecharge @CustomerId INT, @Amount DECIMAL(10,2) AS BEGIN IF EXISTS (SELECT 1 FROM Customers WHERE Id = @CustomerId AND Balance >= @Amount) PRINT 'Success'; ELSE PRINT 'Insufficient Balance'; END
Daraz Order Processing
- Idea Used: DataAdapter + DataSet (Disconnected Model)
- How: Daraz’s mobile app fetches order details offline (using
DataSet) and syncs with the server later. This avoids constant network calls in low-connectivity areas. - Code Trace:
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Orders WHERE UserId=1", conn); DataSet ds = new DataSet(); adapter.Fill(ds, "Orders"); // Fills DataSet offline
Connected vs. Disconnected Models
| Feature | Connected (DataReader) | Disconnected (DataSet/DataTable) |
|---|---|---|
| Performance | Faster (streams data row-by-row) | Slower (loads all data into memory) |
| Memory Usage | Low (no full dataset in memory) | High (entire dataset cached) |
| Use Case | Read-heavy apps (e.g., Ncell’s recharge history) | Offline apps (e.g., Daraz’s order tracking) |
| Network Dependency | Requires open connection | Can work offline (syncs later) |
| Example | SqlDataReader for displaying a table |
DataSet for editing data in a WinForms app |
Step-by-Step: Fetching Data with SqlDataReader
Scenario: Display all users from a Users table in a console app.
using System;
using System.Data.SqlClient;
class Program {
static void Main() {
string connStr = "Server=localhost;Database=TestDB;Integrated Security=True;";
using (SqlConnection conn = new SqlConnection(connStr)) {
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Users", conn);
SqlDataReader reader = cmd.ExecuteReader();
// Step 1: Check if there’s data
if (reader.HasRows) {
while (reader.Read()) { // Moves to next row
Console.WriteLine($"ID: {reader["Id"]}, Name: {reader["Username"]}");
}
}
reader.Close(); // Critical: Releases resources
}
}
}
State After Each Step
- After
conn.Open():
flowchart LR A["SqlConnection"] -->|"State=Open"| B[Database (Connection Established)]
- After
ExecuteReader():
flowchart TD
A[SqlCommand
(ExecuteReader(""))] -->|"Opens Stream"| B[SqlDataReader
(Forward-only, Read-only)]
B -->|"Current Position"| C[Users Table
Row 1: john_doe]- After
reader.Read()(first iteration):Id Username Email 2 jane_smith jane@example.com
Handling Transactions (Bank Transfer Example)
Scenario: Transfer ₹1000 from Account A to Account B in a bank app.
using (SqlConnection conn = new SqlConnection(connStr)) {
conn.Open();
SqlTransaction transaction = conn.BeginTransaction(); // Start transaction
try {
SqlCommand cmd1 = new SqlCommand(
"UPDATE Accounts SET Balance = Balance - 1000 WHERE AccountId = 1",
conn, transaction);
SqlCommand cmd2 = new SqlCommand(
"UPDATE Accounts SET Balance = Balance + 1000 WHERE AccountId = 2",
conn, transaction);
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
transaction.Commit(); // All commands succeed
Console.WriteLine("Transfer successful!");
}
catch (Exception) {
transaction.Rollback(); // Undo if any command fails
Console.WriteLine("Transfer failed!");
}
}
State After Each Step
- After
BeginTransaction():
flowchart TD
A[SqlConnection
(BeginTransaction(""))] -->|Transaction
Pending| B[Database
(Locks Acquired)]
B -->|"Uncommitted"| C[Accounts Table
(No Changes)]- After
Commit()(Success):AccountId Balance
Stored Procedures: Why Use Them?
| Advantage | Disadvantage |
|---|---|
| Performance: Pre-compiled SQL | Maintenance: Requires DB access |
| Security: Reduces SQL injection risk | Complexity: Harder to debug |
| Reusability: Called from multiple apps | Portability: DB-specific |
Example: Stored procedure for user login.
CREATE PROCEDURE AuthenticateUser
@Username NVARCHAR(50), @Password NVARCHAR(50)
AS
BEGIN
IF EXISTS (SELECT 1 FROM Users WHERE Username = @Username AND Password = @Password)
SELECT 'Success' AS Result;
ELSE
SELECT 'Failed' AS Result;
END
C# Call:
SqlCommand cmd = new SqlCommand("AuthenticateUser", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@Username", "admin");
cmd.Parameters.AddWithValue("@Password", "pass123");
object result = cmd.ExecuteScalar();
Console.WriteLine(result); // "Success" or "Failed"
LINQ to SQL: Bridging C# and SQL
LINQ to SQL maps C# classes to database tables, enabling type-safe queries.
Example: Query users with balance > ₹5000.
// 1. Define a DataContext (maps to database)
DataContext db = new DataContext(connStr);
// 2. Define a LINQ query
var highBalanceUsers = from user in db.GetTable<User>()
where user.Balance > 5000
select user;
// 3. Execute and display
foreach (var user in highBalanceUsers) {
Console.WriteLine(user.Username);
}
Generated SQL:
SELECT * FROM Users WHERE Balance > 5000
Exam Tip
Connection Management:
- Always use
usingblocks to auto-close connections (avoids memory leaks). - Example:
using (SqlConnection conn = new SqlConnection(connStr)) { ... }
- Always use
Parameterized Queries:
- Never concatenate strings for SQL (vulnerable to injection).
- Use
@paramsyntax every time.
Transactions:
- Remember: Commit only if all steps succeed; otherwise, Rollback.
DataReader vs. DataAdapter:
DataReader: For forward-only, read-heavy operations.DataAdapter: For disconnected scenarios (e.g., WinForms apps).
Stored Procedures:
- Know how to call them in C# (
CommandType.StoredProcedure). - Understand their advantages (security, performance).
- Know how to call them in C# (
LINQ to SQL:
- Recognize how C# queries translate to SQL.
- Example:
whereclauses map toWHEREin SQL.
Visual Summary:
mindmap
root((ADO.NET))
Connection
Open/Close
ConnectionString
Command
ExecuteNonQuery
ExecuteReader
Parameters
DataReader
Read()
GetXXX()
DataAdapter
Fill(DataSet)
Update(DataSet)
Transactions
BeginTransaction
Commit/Rollback
Stored Procedures
Pre-compiled SQL
Security
LINQ to SQL
Type-safe queries
DataContextBased on the TU BITM syllabus for .NET Programming (IT275), unit 6.
Discussion
Loading…