Custom Mapping
What this page covers#
This page explains how to take full control over result mapping by supplying your own mapper.
Why this exists#
Auto mapping is great for the common case, but sometimes you need more control.
Custom mapping exists for cases where:
- the result shape does not line up neatly with your target type
- you want to transform values while reading
- you want explicit control over null handling or conversion
- you want to optimise a hot path
- you simply prefer to map rows yourself
The simple version#
With custom mapping, you provide a delegate that receives a SqlDataReader and returns the object for that row.
That means you can read values however you want.
Start with an inline lambda#
var users = await db.ListAsync( "dbo.usp_User_List", static reader => new User { UserId = reader.GetInt32(reader.GetOrdinal("UserId")), DisplayName = reader.GetString(reader.GetOrdinal("DisplayName")) });
GetOrdinal("UserId") asks the reader for the position of the UserId column, and GetInt32(...) then reads that column as an int.
You can also map by ordinal position#
var users = await db.ListAsync( "dbo.usp_User_List", static reader => new User { UserId = reader.GetInt32(0), DisplayName = reader.GetString(2) });
Both styles work because your mapper receives the real SqlDataReader.
Move to a method when the mapping grows#
var users = await db.ListAsync( "dbo.usp_User_List", UserMapping.MapUser);
public static class UserMapping { public static User MapUser(SqlDataReader reader) { return new User { UserId = reader.GetInt32(reader.GetOrdinal("UserId")), DisplayName = reader.GetString(reader.GetOrdinal("DisplayName")) }; } }
Why custom mapping is useful#
Custom mapping is useful when you want full row-materialisation control without changing the rest of MooDb's calling style.
You still use SingleAsync, ListAsync, or QueryMultipleAsync, but you decide exactly how each row becomes a .NET object.
Important notes#
- Custom mapping bypasses MooDb auto mapping.
- Your mapper runs once per row.
- You can map by column name or by ordinal.
- Inline lambdas are good for short local mappings.
- Reusable mapping methods or mapping classes are better once the mapper grows.