Tutorials ADO.NET Core Tutorial
High-Performance APIs — Complete Guide
High-Performance APIs — Complete Guide: free step-by-step lesson with examples, common mistakes, and interview tips — part of ADO.NET Core Tutorial on Toolliyo Academy.
On this page
Introduction
High-Performance APIs — Complete Guide is essential for .NET developers building ShopNest.DataAccess — Enterprise High-Performance Data Platform — Toolliyo's 100-article ADO.NET Core master path covering SqlConnection, stored procedures, transactions, connection pooling, ASP.NET Core integration, Azure SQL, and ten enterprise portfolio projects. Every article includes minimum two enterprise real-world examples (ICICI banking, TCS ERP reporting, insurance batch, legacy modernization).
In Indian delivery projects (TCS, Infosys, Wipro), interviewers expect high-performance apis with real banking transfers, ERP GL reports, or legacy stored procedure modernization — not toy animal demos. This article delivers production depth on Payments.
After this article you will
- Explain High-Performance APIs in plain English and in SQL Server / ADO.NET terms
- Implement high-performance apis in ShopNest.DataAccess — Enterprise High-Performance Data Platform (Payments)
- Compare SQL-concat / sync anti-patterns vs production-ready parameterized async ADO.NET
- Answer fresher, mid-level, and senior ADO.NET and SQL Server interview questions confidently
- Connect this lesson to Article 51 and the 100-article ADO.NET Core roadmap
Prerequisites
- Software: .NET 8 SDK, SQL Server Express / LocalDB or Docker, SSMS or Azure Data Studio
- Knowledge: C# basics and SQL Server
- Previous: Article 49 — Memory Optimization — Complete Guide
- Time: 28 min reading + 30–45 min hands-on in SSMS
Concept deep-dive
Level 1 — Analogy
High-Performance APIs on ShopNest.DataAccess adds high-performance SQL Server data access for enterprise high-performance apis modules.
Level 2 — Technical
High-Performance APIs tunes ShopNest throughput — pool sizing, execution plans, covering indexes, SqlBulkCopy, and streaming million-row reports without RAM spikes.
Level 3 — Data platform view
[ASP.NET Core API / MVC Controller]
▼
[Application Service — IOrderRepository interface]
▼
[ADO.NET Repository — SqlConnection + SqlCommand + SqlParameter]
▼
[SQL Server — Tables · Indexes · Stored Procedures · Transactions]
▼
[Connection Pool · Read Replica · Azure SQL · Serilog + SQL Profiler]
Common misconceptions
❌ MYTH: ADO.NET is obsolete — always use EF Core.
✅ TRUTH: ADO.NET wins for stored procedures, bulk load, streaming reports, and legacy SQL — EF Core for rapid CRUD.
❌ MYTH: String concatenation is fine if you escape quotes.
✅ TRUTH: Always SqlParameter — SQL injection is the #1 data breach vector in Indian banking apps.
❌ MYTH: Sync database calls are fine in ASP.NET Core.
✅ TRUTH: Use async ADO.NET end-to-end — sync calls block thread pool under load.
Project structure
ShopNest.DataAccess/
├── src/
│ ├── ShopNest.DataAccess.Api/ ← ASP.NET Core Web API
│ ├── ShopNest.DataAccess.Core/ ← Repository interfaces + DTOs
│ ├── ShopNest.DataAccess.AdoNet/ ← SqlConnection, SPs, transactions
│ ├── ShopNest.DataAccess.Reports/ ← Streaming readers, GL reports
│ └── ShopNest.DataAccess.Tests/ ← Testcontainers SQL integration
├── sql/
│ ├── migrations/
│ └── stored-procedures/ ← usp_Orders_*, usp_Payments_*
└── docker-compose.yml ← SQL Server 2022
Hands-on implementation — Payments
Write High-Performance APIs in ShopNest.DataAccess for Payments: SqlConnection/SqlCommand with parameters, async calls, and verify in SSMS with execution plan.
- Open ShopNest.DataAccess repository for this lesson module.
- Use SqlConnection with await using and connection string from IConfiguration.
- Add SqlParameter for every user input — never string concatenation.
- Use ExecuteReaderAsync for reads; transactions for multi-statement writes.
- Verify in SSMS — check execution plan, row counts, and connection pool metrics.
Anti-pattern (SQL concat, sync calls, DataSet for huge reports)
// ❌ BAD — SQL concat, sync call, no disposal
public List<Order> GetOrders(string status) {
var conn = new SqlConnection(_connStr);
conn.Open();
var cmd = new SqlCommand("SELECT * FROM Orders WHERE Status = '" + status + "'", conn);
var reader = cmd.ExecuteReader(); // sync, blocks thread pool
// connection never disposed — pool exhaustion under load
return Parse(reader);
}
Production-style ADO.NET data access
// ✅ CORRECT — High-Performance APIs on ShopNest (Payments)
public async Task<IReadOnlyList<OrderDto>> GetByStatusAsync(string status, CancellationToken ct) {
await using var conn = new SqlConnection(_config.GetConnectionString("ShopNestDb"));
await conn.OpenAsync(ct);
await using var cmd = new SqlCommand("usp_Orders_GetByStatus", conn) {
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("@Status", SqlDbType.NVarChar, 20).Value = status;
var list = new List<OrderDto>();
await using var reader = await cmd.ExecuteReaderAsync(ct);
while (await reader.ReadAsync(ct))
list.Add(new OrderDto(reader.GetInt32(0), reader.GetDecimal(1), reader.GetString(2)));
return list;
}
Complete example
// Minimal API + ADO.NET repository
app.MapGet("/api/orders", async (IOrderRepository repo) => Results.Ok(await repo.GetPendingAsync()));
SQL performance and connection management — High-Performance APIs
- Connection pooling — default enabled; never disable without load testing; watch pool exhaustion (error 10053/10054)
- Parameterized queries — always use SqlParameter; prevents SQL injection and enables plan cache reuse
- Async — ExecuteReaderAsync/ExecuteNonQueryAsync free thread pool under load
- CommandBehavior.SequentialAccess — stream large BLOB/text columns without loading full row into memory
- Indexes — align with WHERE/JOIN columns; use SQL Server DMVs to find missing indexes
Real-World Example 1 — TCS ERP Monthly GL Reporting
MANDATORY enterprise scenario (Enterprise ERP): High-Performance APIs in ShopNest.DataAccess Payments.
Business problem
Finance teams run month-end General Ledger reports across 200+ cost centers. Report queries join 12 tables and return 2M rows — EF Core materializes entire graphs into memory. ADO.NET SqlDataReader streams rows to CSV/PDF generators with constant memory.
Architecture
[Report Scheduler] → [GlReportRepository]
→ EXEC usp_GenerateMonthlyGL @Year, @Month, @CostCenterId
→ SqlDataReader forward-only stream → IAsyncEnumerable
→ Bulk copy to staging → SSRS / Excel export
Read uncommitted avoided; NOLOCK only on read replica for analytics.
Production ADO.NET code
public async IAsyncEnumerable<GlLineDto> StreamGlReportAsync(int year, int month, [EnumeratorCancellation] CancellationToken ct)
{
await using var conn = new SqlConnection(_readReplicaConnectionString);
await conn.OpenAsync(ct);
await using var cmd = new SqlCommand("usp_GenerateMonthlyGL", conn)
{
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.Add("@Year", SqlDbType.Int).Value = year;
cmd.Parameters.Add("@Month", SqlDbType.Int).Value = month;
await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess, ct);
while (await reader.ReadAsync(ct))
{
yield return new GlLineDto(
reader.GetString(0),
reader.GetDecimal(1),
reader.GetDateTime(2));
}
}
Outcome
Memory flat at 80MB for 2M-row report vs 1.2GB EF Core ToList(); report runtime cut from 14 min to 3 min.
Real-World Example 2 — Razorpay-Style Payment Reconciliation
MANDATORY enterprise scenario (Payment Gateway): High-Performance APIs in ShopNest.DataAccess Payments.
Business problem
End-of-day reconciliation matches 1M gateway transactions against internal ledger. ADO.NET table-valued parameters feed set-based MERGE in SQL Server — impossible to express efficiently in LINQ.
Architecture
Gateway CSV → TVP dbo.TransactionBatch → usp_ReconcilePayments
→ MERGE Payments.Ledger → Output mismatches to ReconciliationExceptions
Production ADO.NET code
var tvp = new SqlParameter("@Batch", SqlDbType.Structured)
{
TypeName = "dbo.TransactionBatchType",
Value = BuildDataTable(transactions)
};
cmd.Parameters.Add(tvp);
await cmd.ExecuteNonQueryAsync(ct);
Outcome
Reconciliation completes in 12 minutes; EF Core prototype timed out at 45 minutes on same hardware.
ADO.NET with ASP.NET Core — High-Performance APIs
Register IPaymentsRepository as Scoped in DI. Never hold SqlConnection across requests. Use IConfiguration for connection strings; User Secrets locally, Azure Key Vault in production.
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddHealthChecks().AddSqlServer(connectionString);
Stored procedures and SQL safety
Enterprise ShopNest modules use versioned stored procedures (usp_ prefix). Never concatenate user input — always SqlParameter. Log slow queries (>500ms) with Serilog and review execution plans in SSMS.
Common errors & fixes
- SQL built with string concatenation from user input — Use SqlParameter with typed values for every dynamic value.
- Not disposing SqlConnection / SqlDataReader — Use await using for connection, command, and reader — return connections to pool.
- Loading million-row reports into DataTable — Stream with SqlDataReader and yield batches; avoid DataSet for large data.
- Hard-coding connection strings in repository classes — IConfiguration + User Secrets locally; Azure Key Vault in production.
Best practices
- 🟢 SqlParameter for every dynamic value — zero string concatenation
- 🟢 await using for SqlConnection, SqlCommand, SqlDataReader — return to pool
- 🟡 Async ADO.NET end-to-end on ASP.NET Core request paths
- 🟡 Stream large reports with SqlDataReader; avoid DataSet for millions of rows
- 🔴 SqlTransaction for multi-statement financial writes with explicit rollback
- 🔴 Connection strings in Key Vault — never committed to Git
Interview questions
Fresher level
Q1: What is High-Performance APIs in ADO.NET Core?
A: High-Performance APIs on ShopNest Payments: SqlConnection lifecycle, SqlCommand with parameters, async execution, and disposal for connection pool health.
Q2: ADO.NET vs EF Core — when to use which?
A: ADO.NET for stored procedures, bulk load, streaming reports, and legacy SQL; EF Core for rapid CRUD and migrations. ShopNest uses both.
Q3: How do you prevent SQL injection in ADO.NET?
A: Always SqlParameter with typed SqlDbType — never string concatenation, even for "trusted" internal tools.
Mid / senior level
Q4: Explain connection pooling and why disposal matters.
A: SqlConnection.Close/Dispose returns the physical connection to the pool. Leaked connections exhaust Max Pool Size and cause timeouts.
Q5: How do you handle transactions in ADO.NET?
A: SqlTransaction with try/commit/catch/rollback; choose isolation level (ReadCommitted default); retry deadlocks with Polly.
Q6: How would you optimize a slow stored procedure report?
A: Check execution plan in SSMS, add covering indexes, avoid SELECT *, stream with SqlDataReader, consider read replica for analytics.
Coding round
Implement a parameterized ADO.NET repository method for ShopNest Payments — show SqlConnection, SqlCommand, SqlParameter, async disposal, and one xUnit integration test.
Summary & next steps
- Article 50: High-Performance APIs — Complete Guide
- Module: Module 5: Performance Optimization · Level: ADVANCED
- Applied to ShopNest.DataAccess — Payments
Previous: Memory Optimization — Complete Guide
Next: ADO.NET with ASP.NET Core MVC — Complete Guide
Practice: Run today's SQL in SSMS with execution plan — commit with feat(adonet): article-050.
FAQ
Q1: What is High-Performance APIs?
High-Performance APIs helps ShopNest.DataAccess implement high-performance Payments data access with Microsoft.Data.SqlClient and SQL Server.
Q2: Do I need EF Core to learn ADO.NET?
No — ADO.NET is the foundation. Many Indian banking and ERP projects still rely on stored procedures wrapped in ADO.NET.
Q3: Is ADO.NET asked in interviews?
Yes — SqlConnection, parameters, transactions, and ADO.NET vs EF appear in TCS, Infosys, and product company .NET rounds.
Q4: Which .NET version?
Examples target .NET 8 LTS with Microsoft.Data.SqlClient and async ADO.NET throughout.
Q5: How does this fit ShopNest.DataAccess?
Article 50 strengthens Payments. By Article 100 you have a portfolio-ready enterprise data layer.
Sign in to ask a question or upvote helpful answers.
No questions yet — be the first to ask!