MapWhen Attribute

November 22, 2025 ยท View on GitHub

The [MapWhen] attribute enables conditional property mapping based on source property values. Properties are only mapped when the specified condition evaluates to true.

Basic Usage

[Facet(typeof(Order))]
public partial class OrderDto
{
    [MapWhen("Status == OrderStatus.Completed")]
    public DateTime? CompletedAt { get; set; }
}

When Status is Completed, CompletedAt is mapped from source. Otherwise, it defaults to null.

Supported Conditions

Boolean Properties

[MapWhen("IsActive")]
public string? Email { get; set; }

Equality Comparisons

[MapWhen("Status == OrderStatus.Completed")]
public DateTime? CompletedAt { get; set; }

[MapWhen("Status != OrderStatus.Cancelled")]
public string? TrackingNumber { get; set; }

Null Checks

[MapWhen("Email != null")]
public string? Email { get; set; }

Numeric Comparisons

[MapWhen("Age >= 18")]
public string? AdultContent { get; set; }

Negation

[MapWhen("!IsDeleted")]
public string? Content { get; set; }

Multiple Conditions

Multiple [MapWhen] attributes on the same property are combined with AND logic:

[MapWhen("IsActive")]
[MapWhen("Status == OrderStatus.Completed")]
public DateTime? CompletedAt { get; set; }

This maps CompletedAt only when IsActive is true AND Status is Completed.

Generated Code

Constructor

// Input
[Facet(typeof(Order))]
public partial class OrderDto
{
    [MapWhen("Status == OrderStatus.Completed")]
    public DateTime? CompletedAt { get; set; }
}

// Generated constructor
public OrderDto(Order source)
{
    Id = source.Id;
    Status = source.Status;
    CompletedAt = (source.Status == OrderStatus.Completed)
        ? source.CompletedAt
        : default;
}

Projection

The same conditional logic is applied in the Projection expression for use with Entity Framework Core:

public static Expression<Func<Order, OrderDto>> Projection => source => new OrderDto
{
    Id = source.Id,
    Status = source.Status,
    CompletedAt = (source.Status == OrderStatus.Completed)
        ? source.CompletedAt
        : default
};

Attribute Properties

PropertyTypeDescription
ConditionstringThe condition expression (required)
Defaultobject?Custom default value when condition is false
IncludeInProjectionboolWhether to include in Projection expression (default: true)

Custom Default Values

[MapWhen("HasPrice", Default = 0)]
public decimal Price { get; set; }

Excluding from Projection

For conditions that can't be translated to SQL:

[MapWhen("IsActive", IncludeInProjection = false)]
public string? Email { get; set; }

Expression Syntax

The condition string uses C# expression syntax:

Supported Operators

  • Comparison: ==, !=, <, >, <=, >=
  • Logical: &&, ||, !

Property Access

  • Simple: IsActive, Status, Email
  • Enum values: OrderStatus.Completed, Status == OrderStatus.Pending

Literals

  • Booleans: true, false
  • Null: null
  • Numbers: 18, 0

Example Use Cases

Status-Dependent Fields

[Facet(typeof(Subscription))]
public partial class SubscriptionDto
{
    public SubscriptionStatus Status { get; set; }

    [MapWhen("Status == SubscriptionStatus.Active")]
    public DateTime? NextBillingDate { get; set; }

    [MapWhen("Status == SubscriptionStatus.Cancelled")]
    public string? CancellationReason { get; set; }
}

Conditional Sensitive Data

[Facet(typeof(Employee))]
public partial class EmployeeDto
{
    public string Name { get; set; }

    [MapWhen("!IsSalaryConfidential")]
    public decimal? Salary { get; set; }
}

Age-Gated Content

[Facet(typeof(User))]
public partial class UserDto
{
    public int Age { get; set; }

    [MapWhen("Age >= 18")]
    public string? AdultPreferences { get; set; }
}

Limitations

  • Method calls in conditions (like string.IsNullOrEmpty()) are not supported
  • Nested property access (like Address.City) is not supported
  • Complex expressions may need to be simplified

Best Practices

  1. Keep conditions simple - Use basic comparisons and boolean checks
  2. Use nullable properties - Since conditions may be false, properties should typically be nullable
  3. Consider EF Core translation - If using projections with a database, ensure conditions can translate to SQL
  4. Test both paths - Write tests for both when conditions are true and false