Link Search Menu Expand Document

Property Handlers


This feature allows you to handle the transformation of class properties and database columns (inbound/outbound). It enables custom conversion between class properties and .NET CLR types.

Each transformation execution receives the actual value and the affected ClassProperty object for additional context.

The following objects are used:

ObjectDescription
IPropertyHandlerAn interface to mark your class as a property handler.
PropertyHandlerAn attribute used to map a property handler to a specific property.
PropertyHandlerMapperA mapper used to map a property handler to a specific property.
FluentMapperA fluent mapper class used to map a property handler to a specific property.

Relevant Use-Cases

Common scenarios that this feature addresses:

Use-CaseDescription
String to Complex-TypeConvert an NVARCHAR column named Address to and from an Address class within the application.
As Type HandlerConvert the Kind property of System.DateTime each time a record is pushed or pulled from the database.

Additional use cases include:

  • Overriding the conversion of monetary columns to a specific .NET type.
  • Querying related child records of parent rows.
  • Triggering updates as a reaction to transformation.
  • Using as a trigger mechanism.
  • Manually overriding the default handler for enumerations.
  • And many more.

How does it works?

When reading data from the database (e.g., ExecuteQuery, Query, BatchQuery), the Get() method is invoked after deserializing the model property. When pushing data to the database (e.g., Insert, Merge, Update), the Set() method is invoked before the actual database operation.

Implementing a Property Handler

Create a class that implements the IPropertyHandler interface.

public class PersonAddressPropertyHandler : IPropertyHandler<string, Address>
{
    public Address Get(string input, PropertyHandlerGetOptions options) =>
        !string.IsNullOrEmpty(input) ? JsonConvert.Deserialize<Address>(input) : null;

    public string Set(Address input, PropertyHandlerSetOptions options) =>
        (input != null) ? JsonConvert.Serialize(input) : null;
}

The handler above converts a string column to a class object and back.

Attaching to a Property

Use the PropertyHandler attribute to attach a property handler to a class property.

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    [PropertyHandler(typeof(PersonAddressPropertyHandler))]
    public Address Address { get; set; }
}

Or use the FluentMapper class, which uses PropertyHandlerMapper internally.

FluentMapper
    .Entity<Person>()
    .PropertyHandler<PersonAddressPropertyHandler>(e => e.Address);

When calling pull operations (e.g., Query, QueryAll, BatchQuery), the Get() method is invoked.

When calling push operations (e.g., Insert, Update, Merge), the Set() method is invoked.

Please visit our Property Handler (Property Level) reference implementation page for the detailed implementation.

Creating a Type-Level Property Handler

The implementation follows the same process as Implementing a Property Handler. Create a class that implements the IPropertyHandler interface.

public class DateTimeKindToUtcPropertyHandler : IPropertyHandler<DateTime?, DateTime?>
{
    public DateTime? Get(DateTime? input, PropertyHandlerGetOptions options) =>
        DateTime.SpecifyKind(input, DateTimeKind.Utc);

    public DateTime? Set(DateTime? input, PropertyHandlerSetOptions options) =>
        DateTime.SpecifyKind(input.GetValueOrDefault(), DateTimeKind.Unspecified);
}

Use the PropertyHandlerMapper class to map it:

PropertyHandlerMapper.Add(typeof(DateTime), new DateTimeKindToUtcPropertyHandler(), true);

Or use the FluentMapper class for type-level mapping, which also uses PropertyHandlerMapper internally.

FluentMapper
    .Type<DateTime>()
    .PropertyHandler<PersonAddressPropertyHandler>();

Please visit our Property Handler (Type Level) reference implementation page for the detailed implementation.