Auto Mapping
What this page covers#
This page explains how MooDb automatically maps result sets to .NET types.
The basic idea#
Auto mapping takes the columns returned by the database and maps them onto your target type.
This works for:
- scalar values
- simple types
- classes with writable properties
- classes with constructors
- classes that combine constructors and writable properties
The goal is to keep mapping simple, predictable, and explicit.
How matching works#
MooDb matches result columns to:
- constructor parameters
- writable properties
Matching is based on name.
Column names are compared to parameter and property names.
Property-based mapping#
If your type has a parameterless constructor and writable properties, MooDb will:
- create an instance
- assign values to matching properties
Example:
public sealed class UserRow { public int UserId { get; set; } public string Email { get; set; } = string.Empty; }
Result columns:
UserId, Email
MooDb will map both columns directly to the properties.
Constructor-based mapping#
If your type has a constructor, MooDb will try to use it.
Example:
public sealed class UserRow { public UserRow(int userId, string email) { UserId = userId; Email = email; } public int UserId { get; } public string Email { get; } }
Result columns:
UserId, Email
MooDb will map columns to constructor parameters.
Mixed mapping#
If your type has both a constructor and writable properties:
- constructor parameters are filled first
- remaining matching columns are applied to writable properties
Example:
public sealed class UserRow { public UserRow(int userId) { UserId = userId; } public int UserId { get; } public string Email { get; set; } = string.Empty; }
Result columns:
UserId, Email
MooDb will:
- use UserId for the constructor
- assign Email to the writable property
Strict vs permissive mapping#
Auto mapping runs in permissive mode by default.
You can enable strict mapping through MooDbContextOptions:
var db = new MooDbContext(connectionString, new MooDbContextOptions { StrictAutoMapping = true });
Strict mode is optional and can be enabled when you want additional safety and validation in your data mapping.
Permissive mapping tells MooDb to map the members it can match and ignore the rest.
Strict mapping tells MooDb to validate that the result shape and target shape line up cleanly before mapping begins.
Strict mapping is not exact type matching. It allows compatible conversions, but prevents mappings that are incomplete, ambiguous, or unsafe.
In permissive mode:
- extra result columns are ignored
- missing writable properties are left at their default values
- mapping succeeds as long as MooDb can build a valid mapping plan
In strict mode:
- every result column must match a constructor parameter or writable property
- every writable property must be supplied by a constructor parameter or matching result column
- incompatible source and destination member types fail validation
- mapping fails early instead of silently ignoring shape differences
Example: extra result column#
Result columns:
UserId, Email, DisplayName, CreatedUtc
Target type:
UserId, Email, DisplayName
Permissive:
- maps UserId, Email, DisplayName
- ignores CreatedUtc
Strict:
- fails
- because CreatedUtc has no matching writable property or constructor parameter
Example: missing result column#
Result columns:
UserId, Email
Target type:
UserId, Email, DisplayName
Permissive:
- maps UserId and Email
- leaves DisplayName at its default value
Strict:
- fails
- because DisplayName is a writable property with no matching column
Example: constructor plus writable property#
public sealed class UserRow { public UserRow(int userId, string email) { UserId = userId; Email = email; } public int UserId { get; } public string Email { get; } public string DisplayName { get; set; } = string.Empty; }
Result columns:
UserId, Email
Permissive:
- constructor is satisfied
- DisplayName remains default
Strict:
- fails
- because DisplayName is writable and not supplied by constructor or result column
Example: type compatibility#
Result column:
UserId as smallint
Target property:
public int UserId { get; set; }
Permissive:
- mapping succeeds
Strict:
- mapping succeeds
- because smallint can be converted to int
Result column:
UserId as nvarchar
Target property:
public int UserId { get; set; }
Permissive:
- may fail later depending on data
Strict:
- fails up front
- because string is not a reliable conversion to int
Custom mapping#
Strict mode applies to automatic mapping.
If you provide your own mapping function, you are responsible for handling the data.
Example:
var users = await db.ListAsync( "dbo.usp_User_List", map: r => new UserRow { UserId = r.GetInt32(r.GetOrdinal("UserId")), Email = r.GetString(r.GetOrdinal("Email")) });
This is not affected by StrictAutoMapping.
Summary#
Permissive mapping:
- maps what matches
- ignores the rest
Strict mapping:
- requires the result and target shapes to align
- fails early when they do not