Skip to content

Static Conversion Methods

Static conversion methods provide an inline, lightweight approach to custom type conversion in DynamoMapper. Static methods are defined directly on the mapper class, making them ideal for simple, mapper-specific conversions.

Overview

The current release supports named static methods on the mapper class for custom conversions.

When to Use Static Methods

Use static conversion methods when:

  • Conversion logic is specific to a single mapper
  • The conversion is simple and doesn't warrant a separate type
  • You prefer co-location of mapping configuration and conversion logic
  • No reuse across multiple mappers is needed

For reusable conversions across multiple mappers, create shared static helpers and call them from the mapper’s static conversion methods.

Required Method Signatures

Static conversion methods must follow exact signatures:

// Conversion TO DynamoDB AttributeValue
static AttributeValue ToMethodName(TProperty value);

// Conversion FROM DynamoDB AttributeValue
static TProperty FromMethodName(AttributeValue value);

Requirements: - Methods must be static - Methods must be declared on the mapper class - Return type and parameter type must match exactly as shown - Both To and From methods must be provided together

Basic Example

using Amazon.DynamoDBv2.Model;

public class OrderStatus
{
    public string Name { get; }
    public int Value { get; }

    private OrderStatus(string name, int value)
    {
        Name = name;
        Value = value;
    }

    public static readonly OrderStatus Pending = new("Pending", 0);
    public static readonly OrderStatus Confirmed = new("Confirmed", 1);
    public static readonly OrderStatus Shipped = new("Shipped", 2);

    public static OrderStatus FromName(string name) => name switch
    {
        "Pending" => Pending,
        "Confirmed" => Confirmed,
        "Shipped" => Shipped,
        _ => throw new ArgumentException($"Unknown status: {name}")
    };
}

public class Order
{
    public Guid OrderId { get; set; }
    public OrderStatus Status { get; set; }
    public decimal Total { get; set; }
}

[DynamoMapper(Convention = DynamoNamingConvention.CamelCase)]
[DynamoField(nameof(Order.Status), ToMethod = nameof(ToOrderStatus), FromMethod = nameof(FromOrderStatus))]
public static partial class OrderMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(Order source);

    public static partial Order FromItem(Dictionary<string, AttributeValue> item);

    // Static conversion methods
    static AttributeValue ToOrderStatus(OrderStatus status)
    {
        return new AttributeValue { S = status.Name };
    }

    static OrderStatus FromOrderStatus(AttributeValue value)
    {
        return OrderStatus.FromName(value.S);
    }
}

Attribute Usage (Phase 1)

DynamoField Attribute

Configure static methods using the [DynamoField] attribute on the mapper class:

[DynamoMapper]
[DynamoField(nameof(Order.Status),
    ToMethod = nameof(ToOrderStatus),
    FromMethod = nameof(FromOrderStatus))]
public static partial class OrderMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(Order source);
    public static partial Order FromItem(Dictionary<string, AttributeValue> item);
}

Properties: - ToMethod - Name of the static method for To conversion (required) - FromMethod - Name of the static method for From conversion (required)

Constraints: - Both ToMethod and FromMethod must be specified together - Method names must reference existing, accessible static methods

DSL Usage (Phase 2)

In Phase 2, static methods will be configurable using the fluent DSL (planned):

[DynamoMapper]
public static partial class OrderMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(Order source);
    public static partial Order FromItem(Dictionary<string, AttributeValue> item);

    static partial void Configure(DynamoMapBuilder<Order> map)
    {
        map.Property(x => x.Status)
           .Using(nameof(ToOrderStatus), nameof(FromOrderStatus));
    }

    static AttributeValue ToOrderStatus(OrderStatus status)
    {
        return new AttributeValue { S = status.Name };
    }

    static OrderStatus FromOrderStatus(AttributeValue value)
    {
        return OrderStatus.FromName(value.S);
    }
}

Multiple Static Conversions

You can define multiple static conversion method pairs in a single mapper:

[DynamoMapper(Convention = DynamoNamingConvention.CamelCase)]
[DynamoField(nameof(Product.Category), ToMethod = nameof(ToCategory), FromMethod = nameof(FromCategory))]
[DynamoField(nameof(Product.Status), ToMethod = nameof(ToProductStatus), FromMethod = nameof(FromProductStatus))]
public static partial class ProductMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(Product source);

    public static partial Product FromItem(Dictionary<string, AttributeValue> item);

    // Category conversion
    static AttributeValue ToCategory(ProductCategory category)
    {
        return new AttributeValue { S = category.Code };
    }

    static ProductCategory FromCategory(AttributeValue value)
    {
        return ProductCategory.FromCode(value.S);
    }

    // Status conversion
    static AttributeValue ToProductStatus(ProductStatus status)
    {
        return new AttributeValue { S = status.Name };
    }

    static ProductStatus FromProductStatus(AttributeValue value)
    {
        return ProductStatus.FromName(value.S);
    }
}

Nullable Types

The generator automatically handles nullable types. Your static methods should work with the non-nullable type:

public class User
{
    public Guid UserId { get; set; }
    public UserRole? Role { get; set; }  // Nullable
}

[DynamoMapper(Convention = DynamoNamingConvention.CamelCase)]
[DynamoField(nameof(User.Role), ToMethod = nameof(ToUserRole), FromMethod = nameof(FromUserRole))]
public static partial class UserMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(User source);

    public static partial User FromItem(Dictionary<string, AttributeValue> item);

    // Methods work with non-nullable UserRole
    static AttributeValue ToUserRole(UserRole role)
    {
        return new AttributeValue { S = role.Name };
    }

    static UserRole FromUserRole(AttributeValue value)
    {
        return UserRole.FromName(value.S);
    }
}

The generator wraps your methods with null checks as needed.

Advanced Example: Complex Value Objects

Static methods work well for complex value objects:

public class Money
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }

    public override string ToString() => $"{Amount:F2} {Currency}";

    public static Money Parse(string value)
    {
        var parts = value.Split(' ');
        return new Money(decimal.Parse(parts[0]), parts[1]);
    }
}

[DynamoMapper(Convention = DynamoNamingConvention.CamelCase)]
[DynamoField(nameof(Product.Price), ToMethod = nameof(ToMoney), FromMethod = nameof(FromMoney))]
public static partial class ProductMapper
{
    public static partial Dictionary<string, AttributeValue> ToItem(Product source);

    public static partial Product FromItem(Dictionary<string, AttributeValue> item);

    static AttributeValue ToMoney(Money money)
    {
        // Store as string: "99.99 USD"
        return new AttributeValue { S = money.ToString() };
    }

    static Money FromMoney(AttributeValue value)
    {
        return Money.Parse(value.S);
    }
}

Diagnostics for Invalid Signatures

DynamoMapper validates static method signatures and emits compile-time diagnostics when a method is missing or has the wrong signature. Check the diagnostic message for the exact fix needed.

Best Practices

  1. Use clear naming conventions

    // Good: Clear what type is being converted
    static AttributeValue ToOrderStatus(OrderStatus status) { }
    static OrderStatus FromOrderStatus(AttributeValue value) { }
    
    // Avoid: Ambiguous method names
    static AttributeValue Convert(OrderStatus status) { }
    

  2. Keep methods simple

  3. Static methods should focus on conversion logic only
  4. Complex business logic belongs elsewhere

  5. Handle errors gracefully

    static OrderStatus FromOrderStatus(AttributeValue value)
    {
        if (string.IsNullOrEmpty(value.S))
            throw new DynamoMappingException("OrderStatus cannot be null or empty");
    
        return OrderStatus.FromName(value.S);
    }
    

  6. Use static methods for mapper-specific conversions

  7. If conversion logic is reused across mappers, use converter types instead

  8. Document complex conversions

    /// <summary>
    /// Converts Money to DynamoDB string format: "amount currency" (e.g., "99.99 USD")
    /// </summary>
    static AttributeValue ToMoney(Money money) { }
    

See Also