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.
-
On the first call for a given
(T, column-shape)pair, the auto-mapper:- inspects
T's public, settable, simple-typed properties via reflection, - matches each one to a result-set column by name,
- builds an expression tree that allocates a
Tand assigns each property from the correspondingSqlDataReader.Get*call, - compiles the tree to a
Func<SqlDataReader, T>delegate, - caches it under a composite key (SQL command text + full type name).
- inspects
-
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].
- Quick prototypes / one-off queries where writing a
CreateObjectis 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.
- 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
Userand a lightweightUserSummary). - You also need to save the type — a
[MapperFor]mapper can carryCreateDataTable/CreateDataRowfor table-valued parameters, while auto-mapping covers reading only.
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");
}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: