Link Search Menu Expand Document

Property Handlers


This is a feature that would allow you to handle the tranformation of the class properties and database columns (inbound/outbound). It allows you to customize the conversion of the class properties and the .NET CLR types.

The execution of the transformation contains the actual values and the affected ClassProperty object to provide more context.

It uses the following objects.

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

Relevant Use-Cases

Below are the common use-cases that can be solved by this functionality.

Use-CaseDescription
String to Complex-TypeImagine you have a column Address of type NVARCHAR and you would like it to be an Address type/class within your application (vice versa).
As Type HandlerImagine you would like to convert the Kind property of the System.DateTime object everytime you pull/push the record towards the database (vice versa).

But, in general, it can handle unlimitted use-cases depends on your own situation.

  • Overriding the monetary columns conversion into a specific .NET type.
  • Querying the related child records of the parent rows.
  • Updating a record as a reaction to the transformation.
  • Can be used as trigger.
  • Manually override the default handler for the enumerations.
  • And many more.

How does it works?

If you are reading a data from the DB (i.e.: ExecuteQuery, Query, BatchQuery), the Get() method will be invoked after deserializing the model propery. On the other hand, if you are pushing a data towards the DB (i.e.: Insert, Merge, Update), the Set() method will be invoked prior the actual DB 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 property handler above is meant for a scenario of converting a string column type into a class object.

Attaching to a Property

To attach a property handler into a class property, simply use the PropertyHandller attribute.

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. It uses the PropertyHandlerMapper underneath.

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

When calling the pull operations (i.e.: Query, QueryAll and BatchQuery), then the Get() method will be invoked.

On the other hand, when you call the push operations (i.e.: Insert, Update and Merge), then the Set() method will be invoked.

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

Creating a Type-Level Property Handler

The process is the same as with Implementing a Property Handler section. 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);
}

Then, use the PropertyHandlerMapper mapper class to map it.

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

Or, use the FluentMapper class type level mapping. Again, it also uses the PropertyHandlerMapper underneath.

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

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