Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
What Is Dapper? The Thin Layer on Top of ADO.NET and Your First Query
We meet Dapper as a set of extension methods on top of ADO.NET: what micro-ORM means, when to pick it over EF Core, setting up .NET 10 + Dapper 2.1.89 + SQLite, the first Query and a short history from 1.x to Dapper.AOT.
Query and Mapping with Dapper: How Do Columns Map to Properties?
We look at how Query<T> maps columns to properties: case-insensitive matching, silently skipped columns, NULL and default values, MatchNamesWithUnderscores and its cache trap, dynamic results and constructor mapping for records.
Single-Row Queries with Dapper: QueryFirst, QuerySingle and ExecuteScalar
We run QueryFirst, QueryFirstOrDefault, QuerySingle and QuerySingleOrDefault side by side with zero, one and many rows to see which exception each throws and when, along with the pitfalls of default(T), nullable types and ExecuteScalar<T>.
Parameters and SQL Injection with Dapper: The Rules of a Safe Query
We see with a real attack why string concatenation opens the door to SQL injection, then look at anonymous objects, DynamicParameters, IN @Ids list expansion, DbString and the $ prefix, along with silent traps such as ORDER BY @Column.
INSERT, UPDATE, DELETE with Dapper Execute: Affected Rows and the New Id
We run INSERT, UPDATE and DELETE with Execute and read the affected row count, get the new id with RETURNING and last_insert_rowid(), look at SQL Server OUTPUT and PostgreSQL RETURNING, and measure why Execute with a list is not bulk.
Async and Connection Management in Dapper: CancellationToken, Pooling and DI
We see with real output why a CancellationToken only travels through CommandDefinition in Dapper, why async on SQLite actually runs synchronously, how connection pooling helps, why sharing one connection breaks, and the connection factory pattern in ASP.NET Core.
Transactions with Dapper: BeginTransaction, Commit, Rollback and TransactionScope
Starting from a half-written order, we set up BeginTransaction, the transaction: parameter, commit and rollback-on-dispose in Dapper, and see with real output why forgetting the parameter stays silent on SQLite, how isolation levels behave, and the async and SQLite traps of TransactionScope.
Multi-Mapping and splitOn in Dapper: Reading Related Data in a Single Query
We see how Dapper's multi-mapping cuts rows into column slices, the default splitOn = "Id" error, custom and comma-separated splitOn, the dictionary pattern for 1-N and null behavior on a LEFT JOIN, and we measure and fix the N+1 problem with a JOIN and IN @ids.
QueryMultiple and GridReader in Dapper: Several Result Sets in One Round Trip
We read several result sets from one command with Dapper's QueryMultiple and GridReader: why grids are read in order and only once, loading a master-detail order in one round trip, joining two result sets in memory, the unbuffered grid trap, and when to pick it over multi-mapping, all with real output.
Buffered and Unbuffered Reads in Dapper: Streaming Large Result Sets
We measure the difference between buffered and unbuffered reads in Dapper: an unbuffered result re-running the query on every enumeration, 191 MB versus near-zero memory on a million rows, IAsyncEnumerable streaming with QueryUnbufferedAsync, cancellation with WithCancellation and the disposed-connection trap, all with real output.
Type Handlers and Custom Mapping in Dapper: Guid, DateTimeOffset and CustomPropertyTypeMap
Why does Dapper blow up when reading Guid, DateTimeOffset and TimeSpan on SQLite? We write interpreters with SqlMapper.TypeHandler<T>, see from the source why SetValue is never called, and cover the DateOnly/TimeOnly story of 2.1.86 and column mapping with CustomPropertyTypeMap.
Stored Procedures with Dapper: Output, Return Value and the Auto-Detection Trap of 2.1.11
SQLite has no stored procedures, so we move to SQL Server in docker: CommandType.StoredProcedure, output and return values with DynamicParameters, how SET NOCOUNT affects Execute, single-word commands mistaken for procedures since 2.1.11, and the function vs procedure difference in PostgreSQL.