Auto Mapping - lobodava/artisan-orm GitHub Wiki

The ReadAs* family (ReadAs<T>, ReadAsList<T>, ReadAsArray<T>, ReadAsDictionary, etc.) reads result sets without requiring a hand-written mapper. Properties on T are matched to result-set columns by name (case-insensitive) using a delegate built once via expression trees and then cached.

Lifecycle

  • On the first call for a given (T, column-shape) pair, the auto-mapper:

    1. inspects T's public, settable, simple-typed properties via reflection,
    2. matches each one to a result-set column by name,
    3. builds an expression tree that allocates a T and assigns each property from the corresponding SqlDataReader.Get* call,
    4. compiles the tree to a Func<SqlDataReader, T> delegate,
    5. caches it under a composite key (SQL command text + full type name).
  • Every later call hits the cache and runs the compiled delegate directly — no reflection, no expression-tree work. After warmup, performance is comparable to a hand-written [MapperFor].

When to use

  • Quick prototypes / one-off queries where writing a CreateObject is overkill.
  • Result sets whose column names happen to match property names exactly.
  • DTOs that already exist for serialization, where you don't want a parallel mapper class.

When to prefer a registered [MapperFor] mapper

  • You need a custom mapping (a column has a different name from the property, or you want to compose a navigation property from several columns).
  • You target Native AOT — the expression-tree compilation path is reflection-heavy and not trim-friendly.
  • You read the same shape into two different shapes (e.g. into both a full User and a lightweight UserSummary).
  • You also need to save the type — a [MapperFor] mapper can carry CreateDataTable / CreateDataRow for table-valued parameters, while auto-mapping covers reading only.

Example

public class User
{
    public int    Id    { get; set; }
    public string Login { get; set; } = null!;
    public string Name  { get; set; } = null!;
    public string Email { get; set; } = null!;
}

// No [MapperFor] attribute — columns and properties are matched by name.
public IList<User> GetAllUsers()
{
    return ReadAsList<User>("select Id, Login, Name, Email from dbo.Users");
}

Auto-mapping for table-valued parameters

There is also an AsDataTable<T> extension that uses the same reflection / expression-tree machinery in the opposite direction — building a DataTable from IEnumerable<T> for use as a TVP. See cmd.AddTableParam for the auto-mapping overloads of TVP creation.


See also:

⚠️ **GitHub.com Fallback** ⚠️