Docs / Auto Mapping

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:

</> C#
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:

</> C#
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:

</> C#
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:

</> C#
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#

</> C#
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:

</> C#
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:

</> C#
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:

</> C#
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