Link Search Menu Expand Document

IPropertyHandler


This interface marks a class as a property handler. It exposes TInput and TResult generic type parameters used by both the Get() and Set() methods.

Generic Types

NameDescription
TInputRefers to the type of the database column. The input type for the getter; the output type for the setter.
TOutputRefers to the type of the data entity property. The input type for the setter; the output type for the getter.

Methods

NameDescription
GetThe method that is being invoked when the outbound transformation is triggered (i.e.: Query, QueryAll and BatchQuery).
SetThe method that is being invoked when the inbound transformation is triggered (i.e.: Insert, Update, Merge and etc).

Both methods accept the ClassProperty to give more context on the current method of the property handler.

Use-Cases

This interface is useful for the following scenarios.

  • Converting a JSON column into a class object.
  • Handling the correct System.DateTime objects Kind.
  • Overriding the monetary columns conversion into a specific .NET type.
  • Querying a 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.

The use-cases are unlimited depending on the situation. Additionally, mapping a property handler to a property overrides the automatic conversion of TypeMapper and enumerations.

How to Implement?

Create a class that implements this interface.

public class AddressPropertyHandler : IPropertyHandler<string, Address>
{
    public Address Get(string input, PropertyHandlerGetOptions options)
    {
        // Handle the transformation from the DB towards the Class
    }

    public string Set(Address input, PropertyHandlerSetOptions options)
    {
        // Handle the transformation from the Class towards the DB
    }
}

Property Level Handling

Property-level transformation is useful when a specific column requires custom handling. For example, given a [dbo].[Person] table where the Address column is of type NVARCHAR(MAX):

Classes

public class Address
{
    public int HouseNo { get; set; }
    public string Country { get; set; }
    public string State { get; set; }
    public string Street { get; set; }
    public string Region { get; set; }
    public int ZipCode { get; set; }
}
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

Handler

public class AddressPropertyHandler : IPropertyHandler<string, Address>
{
    public Address Get(string input, PropertyHandlerGetOptions options)
    {
        return JsonConvert.DeserializeObject<Address>(input);
    }

    public string Set(Address input, PropertyHandlerSetOptions options)
    {
        return JsonConvert.SerializeObject(input);
    }
}
using (var connection = new SqlConnection(connectionString))
{
	var person = connection.Query<Person>(10045);
	Console.WriteLine($"Name: {person.Name}, Address: {person.Address.Street}, {person.Address.Region}, {person.Address.Country} ({person.Address.ZipCode})")
}

Mapping

Via the PropertyHandlerMapper class.

PropertyHandlerMapper
    .Add<Person, AddressPropertyHandler>(e => e.Address, true);

Or, via the FluentMapper class.

FluentMapper
    .Entity<Person>()
    .PropertyHandler<AddressPropertyHandler>(e => e.Address, true);

Or, via an explicit ClassHandler attribute.

publi class Person
{
    ...

    [ClassHandler(typeof(AddressPropertyHandler))]
    public Address Address { get; set; }

    ...
}

When you call any of the fetch (Query, QueryAll and BatchQuery) or push (Insert, Update, Merge) operations, the methods Get() and Set() of the property handler will be invoked immediately.

Type Level Handling

Type-level handling applies a transformation to all properties of a given .NET CLR type across the entire application. This is useful for cross-cutting concerns such as normalizing System.DateTime Kind values on every read.

Use the PropertyHandlerMapper class to register the mapping.

The following example converts all DateTime properties to UTC on every read operation.

Handler

public class DateTimeKindToUtcPropertyHandler : IPropertyHandler<DateTime?, DateTime?>
{
    public DateTime? Get(DateTime? input, PropertyHandlerGetOptions options)
    {
        // Reading from DB, setting the class
        return input.HasValue ? DateTime.SpecifyKind(input.Value, DateTimeKind.Utc) : null;
    }

    public DateTime? Set(DateTime? input, PropertyHandlerSetOptions options)
    {
        // Reading from class, setting back to DB
        return input.HasValue ? DateTime.SpecifyKind(input.Value, DateTimeKind.Unspecified) : null;
    }
}

Mapping

Via the PropertyHandlerMapper class.

PropertyHandlerMapper
    .Add<DateTime, DateTimeKindToUtcPropertyHandler>(true);

Or, via the FluentMapper class.

FluentMapper
    .Type<DateTime>()
    .PropertyHandler<DateTimeKindToUtcPropertyHandler>(true);