Tutorials ADO.NET Core Tutorial
Reporting Systems — Complete Guide
Reporting Systems — 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
Reporting Systems — 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 reporting systems with real banking transfers, ERP GL reports, or legacy stored procedure modernization — not toy animal demos. This article delivers production depth on Notifications.
After this article you will
- Explain Reporting Systems in plain English and in SQL Server / ADO.NET terms
- Implement reporting systems in ShopNest.DataAccess — Enterprise High-Performance Data Platform (Notifications)
- 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 63 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 61 — CQRS with ADO.NET — Complete Guide
- Time: 28 min reading + 30–45 min hands-on in SSMS
Concept deep-dive
Level 1 — Analogy
Reporting Systems on ShopNest.DataAccess adds high-performance SQL Server data access for enterprise reporting systems modules.
Level 2 — Technical
Reporting Systems scales ShopNest enterprise data — read replicas, warehouse ETL, background workers, and strangler-fig legacy modernization on Notifications.
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 — Notifications
Write Reporting Systems in ShopNest.DataAccess for Notifications: 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 — Reporting Systems on ShopNest (Notifications)
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
// Reporting Systems — ShopNest.DataAccess (Notifications)
await using var conn = new SqlConnection(_connectionString);
await conn.OpenAsync(ct);
SQL performance and connection management — Reporting Systems
- 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): Reporting Systems in ShopNest.DataAccess Notifications.
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 — Insurance Claims Batch Processing
MANDATORY enterprise scenario (Insurance (LIC-style)): Reporting Systems in ShopNest.DataAccess Notifications.
Business problem
Nightly batch validates 500K claims against policy rules. SqlBulkCopy inserts staging rows; stored procedure usp_ValidateClaims runs set-based SQL; invalid claims roll back in one transaction per batch of 5,000.
Architecture
CSV ingest → SqlBulkCopy → Staging.Claims
→ BEGIN TRANSACTION per batch
→ EXEC usp_ValidateClaims @BatchId
→ COMMIT or ROLLBACK + ErrorLog
Production ADO.NET code
using var bulk = new SqlBulkCopy(conn, SqlBulkCopyOptions.TableLock, tx)
{
DestinationTableName = "Staging.Claims",
BatchSize = 5000,
BulkCopyTimeout = 600
};
await bulk.WriteToServerAsync(dataTable, ct);
await using var validate = new SqlCommand("usp_ValidateClaims", conn, tx) { CommandType = CommandType.StoredProcedure };
validate.Parameters.Add("@BatchId", SqlDbType.Int).Value = batchId;
await validate.ExecuteNonQueryAsync(ct);
Outcome
Batch window reduced from 6 hours to 90 minutes; zero partial batches after explicit transaction boundaries.
ADO.NET with ASP.NET Core — Reporting Systems
Register INotificationsRepository 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 Reporting Systems in ADO.NET Core?
A: Reporting Systems on ShopNest Notifications: 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 Notifications — show SqlConnection, SqlCommand, SqlParameter, async disposal, and one xUnit integration test.
Summary & next steps
- Article 62: Reporting Systems — Complete Guide
- Module: Module 7: Advanced Enterprise Topics · Level: ADVANCED
- Applied to ShopNest.DataAccess — Notifications
Previous: CQRS with ADO.NET — Complete Guide
Next: Data Warehousing Concepts — Complete Guide
Practice: Run today's SQL in SSMS with execution plan — commit with feat(adonet): article-062.
FAQ
Q1: What is Reporting Systems?
Reporting Systems helps ShopNest.DataAccess implement high-performance Notifications 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 62 strengthens Notifications. 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!