Attributes Reference
The complete reference for every Pragmatic.Mapping attribute, the property-matching rules the generator follows, the customization hooks it emits, the EF Core query helpers, the mutation helpers, and the diagnostics. For the mental model and a guided first mapping, start with Concepts and Getting Started.
[MapFrom<TSource>]
Section titled “[MapFrom<TSource>]”Generates mapping from a source type (typically an entity) to the decorated DTO.
Generated members:
static TDto FromEntity(TSource entity)— static factory methodstatic Func<TSource, TDto> Selector— delegate for in-memory LINQ- Extension methods:
entity.ToDto(),IEnumerable<T>.ToDto(),List<T>.ToDto(),T[].ToDto()
Usage:
[MapFrom<User>]public partial class UserDto{ public Guid Id { get; init; } public string Name { get; init; } = "";}Requirements: The type must be declared as partial. Supports class, record, struct, and record struct.
[MapTo<TTarget>]
Section titled “[MapTo<TTarget>]”Generates mapping from the DTO to a target type (typically an entity).
Generated members:
TTarget ToEntity()— creates a new entity instancevoid ApplyTo(TTarget entity)— updates an existing entity (partial update semantics for nullable properties)
Usage:
[MapTo<User>]public partial record CreateUserDto{ public string Email { get; init; } = ""; public string FirstName { get; init; } = "";}
// Creates a new entityUser user = dto.ToEntity();
// Updates an existing entitydto.ApplyTo(existingUser);Note: ID properties (Id, {EntityName}Id) are excluded from ToEntity() by default. Use [MapProperty] on the ID property to force inclusion.
[GenerateProjection]
Section titled “[GenerateProjection]”Generates Expression<Func<TSource, TDto>> Projection for EF Core Select() queries. Requires [MapFrom<T>] on the same type.
Projections translate to SQL, so only SQL-translatable operations are supported. Converters ([MapConverter]), format strings, and CustomizeMapping() are excluded from projections with compile-time warnings.
Usage:
[MapFrom<Property>][GenerateProjection]public partial class PropertySummaryDto{ public Guid Id { get; init; } public string Name { get; init; } = ""; public string City { get; init; } = ""; public int StarRating { get; init; }}
// SQL-optimized queryvar dtos = await db.Properties .Where(p => p.IsActive) .Select(PropertySummaryDto.Projection) .ToListAsync();See Projections for details.
[GenerateBodyOnlyVariant]
Section titled “[GenerateBodyOnlyVariant]”Generates an additional FromEntityBodyOnly() method that maps only scalar properties, skipping collections, nested DTOs, and dictionaries. Useful for mutation scenarios.
[MapFrom<Reservation>][GenerateBodyOnlyVariant]public partial class ReservationSummaryDto{ public Guid Id { get; init; } public decimal TotalAmount { get; init; } // Mapped in both public ReservationStatus Status { get; init; } // Mapped in both
[MapProperty("Guest.FirstName")] public string GuestFirstName { get; init; } = ""; // Skipped in BodyOnly}[MapProperty]
Section titled “[MapProperty]”Customizes how a single property is mapped.
Capabilities:
| Feature | Syntax | Example |
|---|---|---|
| Explicit source path | [MapProperty("Address.City")] | Flatten navigation |
| Concatenation | [MapProperty("FirstName", "LastName")] | Multiple paths joined |
| Custom separator | Separator = ", " | "Doe, John" |
| Format string | Format = "yyyy-MM-dd" | Date formatting via .ToString(format) |
| Default value | Default = "N/A" | Fallback for nullable to non-nullable |
| Target path (MapTo) | Target = "Customer.Name" | Map to nested entity property |
Examples from Showcase:
// Navigation flattening (Showcase: RoomTypeSummaryDto)[MapProperty("Property.Name")]public string PropertyName { get; init; } = "";
// Format string (Showcase: PropertyDetailDto)[MapProperty("CreatedAt", Format = "yyyy-MM-dd")]public string CreatedDate { get; init; } = "";
// Concatenation[MapProperty(nameof(User.FirstName), nameof(User.LastName))]public string FullName { get; init; } = "";
// Default value for nullable source[MapProperty(nameof(User.MiddleName), Default = "N/A")]public string MiddleName { get; init; } = "";[MapIgnore]
Section titled “[MapIgnore]”Excludes a property from mapping. Use for computed properties or properties populated elsewhere.
[MapFrom<Guest>]public partial class GuestDto{ public string FirstName { get; init; } = ""; public string LastName { get; init; } = "";
[MapIgnore] public string FullName => $"{FirstName} {LastName}";}[MapConverter<TConverter>]
Section titled “[MapConverter<TConverter>]”Specifies a custom IValueConverter<TSource, TTarget> for a property. The converter must have a parameterless constructor.
Not supported in projections (PRAG0320 warning) — converters cannot be translated to SQL.
// 1. Implement IValueConverterpublic sealed class MoneyToStringConverter : IValueConverter<decimal, string>{ public string Convert(decimal source) => source.ToString("N2"); public decimal ConvertBack(string target) => decimal.TryParse(target, out var result) ? result : 0m;}
// 2. Use on a property (Showcase: InvoiceSummaryDto)[MapFrom<Invoice>]public partial class InvoiceSummaryDto{ [MapProperty(nameof(Invoice.TotalAmount))] [MapConverter<MoneyToStringConverter>] public string TotalFormatted { get; init; } = "";}See Custom Converters for the full guide.
[MapConstructor]
Section titled “[MapConstructor]”Forces the generator to use a specific constructor when creating entity instances in [MapTo] scenarios. When omitted, the generator uses a best-match algorithm.
public class User{ public User() { }
[MapConstructor] public User(int id, string email) { Id = id; Email = email; }}Property Matching Rules
Section titled “Property Matching Rules”The generator resolves DTO properties to source properties in this order:
| Priority | Strategy | Example |
|---|---|---|
| 1 | [MapProperty] explicit path | [MapProperty("Address.City")] on City |
| 2 | [MapConverter<T>] | Custom type conversion |
| 3 | Direct name match (case-insensitive) | Name maps to Name |
| 4 | Flattening convention | AddressCity maps to Address.City |
| 5 | Concatenation convention | FullName maps to FirstName + " " + LastName |
Unmatched properties generate a PRAG0303 warning and receive default.
Auto-Default for Nullable to Non-Nullable
Section titled “Auto-Default for Nullable to Non-Nullable”When the source is nullable and the target is not, the generator applies sensible defaults: string? to string uses ?? "", numeric/bool/date/Guid types use .GetValueOrDefault(), enums use .GetValueOrDefault(). For explicit control, use [MapProperty(Default = "value")].
Type Conversions
Section titled “Type Conversions”The generator detects type mismatches and applies conversions automatically. Supported conversions include: numeric/enum/Guid/bool/DateTime to/from string (via .ToString() / .Parse()), DateTime to/from DateOnly and TimeOnly, and implicit widening (int to long). See Feature Matrix for the full table.
Collections and Nested DTOs
Section titled “Collections and Nested DTOs”Collections (List<T>, T[], IEnumerable<T>, HashSet<T>, Dictionary<K,V>) are mapped element-by-element. Cross-collection type conversions (List<T> to T[] and vice versa) are supported.
When a DTO property is itself a DTO with [MapFrom<T>], the generator calls FromEntity() recursively. Circular references are detected at compile time (PRAG0313 info) and handled with instance tracking.
Customization Hooks
Section titled “Customization Hooks”The generator emits two partial methods that you can implement:
[MapFrom<User>]public partial class UserDto{ public Guid Id { get; init; } public string Name { get; init; } = "";
// Called BEFORE mapping. Return a result to short-circuit. static partial void BeforeMapping(User source, ref UserDto? result) { if (source.IsDeleted) { result = new UserDto { Name = "[Deleted]" }; // Mapping stops here } }
// Called AFTER mapping. Modify the context to adjust values. static partial void CustomizeMapping(User source, ref UserDtoMappingContext ctx) { ctx.Name = ctx.Name.ToUpperInvariant(); }}The generator creates a {TypeName}MappingContext struct with mutable fields for each mapped property. CustomizeMapping receives this struct by ref, allowing you to modify any mapped value before the final DTO is constructed.
Note: CustomizeMapping is ignored in [GenerateProjection] (PRAG0319 warning) because Expression Trees cannot contain arbitrary C# logic.
EF Core query helpers (Pragmatic.Mapping.EFCore)
Section titled “EF Core query helpers (Pragmatic.Mapping.EFCore)”The Pragmatic.Mapping.EFCore package provides extension methods for IQueryable<T> that work with generated projections:
| Method | Description |
|---|---|
SelectDto(projection) | Projects query using expression |
ToListDtoAsync(projection) | Async projection to list |
FirstOrDefaultDtoAsync(projection) | Async single item projection |
SingleOrDefaultDtoAsync(projection) | Async single-or-default projection |
ToArrayDtoAsync(projection) | Async projection to array |
ToPagedDtoAsync(projection, page, size) | Paginated projection with metadata |
ToSliceDtoAsync(projection, offset, limit) | Offset/limit projection |
All methods include OpenTelemetry ActivitySource tracing with Pragmatic.Mapping activity names.
// Paginated resultsvar page = await db.Properties .Where(p => p.IsActive) .OrderBy(p => p.Name) .ToPagedDtoAsync(PropertySummaryDto.Projection, pageNumber: 1, pageSize: 20);
page.Items // IReadOnlyList<PropertySummaryDto>page.TotalCount // Total matching itemspage.TotalPages // Computed page countpage.HasNextPage // Pagination flagThe Include problem:
FromEntity()operates on in-memory objects — navigation properties not loaded via.Include()will benull, producing silent data loss. PreferProjectionfor EF Core queries, or use explicit.Include(). See Common Mistakes.
Mutation Helpers
Section titled “Mutation Helpers”MutationHelpers provides composable methods for applying DTO changes to entities:
MapOneToOne— Maps a 1:1 reference navigation. If the DTO value is null, the navigation is set to null. Otherwise updates in-place or creates new.MapOneToMany— Synchronizes a collection navigation with three strategies:CollectionStrategy.Sync— Add new, update existing, remove missing (default)CollectionStrategy.AddOnly— Add new, update existing, never removeCollectionStrategy.Replace— Clear all and recreate from DTO
MutationHelpers.MapOneToOne( dto.ShippingAddress, () => order.ShippingAddress, addr => order.ShippingAddress = addr, addrDto => addrDto.ToEntity(), (addrDto, addr) => addrDto.ApplyTo(addr));
MutationHelpers.MapOneToMany( dto.Lines, order.Lines, d => d.Id, e => e.Id, lineDto => lineDto.ToEntity(), (lineDto, line) => lineDto.ApplyTo(line), CollectionStrategy.Sync);Diagnostics
Section titled “Diagnostics”| ID | Severity | Description |
|---|---|---|
| PRAG0300 | Error | Type must be declared as partial |
| PRAG0303 | Warning | No matching source property (will use default) |
| PRAG0307 | Warning | Required property not mapped in [MapTo] |
| PRAG0309 | Error | Nested type missing [MapFrom] |
| PRAG0313 | Info | Circular reference detected, using instance tracking |
| PRAG0316 | Error | No suitable [MapTo] constructor (required parameter has no matching property) |
| PRAG0319 | Warning | CustomizeMapping ignored in Projection |
| PRAG0320 | Warning | [MapConverter] not supported in Projection |
| PRAG0321 | Warning | Format string not translatable to SQL |
| PRAG0322 | Warning | Complex dictionary value not supported |
| PRAG0324 | Info | ID property excluded from ToEntity() |