IPropertyHandler
This interface is used to mark a class to be a property handler object. This interface has TInput
and TResult
generic types in which being used at both the Get()
and Set()
methods.
Generic Types
Below is the list of generic types.
Name | Description |
---|---|
TInput | Refers to the type of the database column. The input type for the getter; the output type for the setter. |
TOutput | Refers to the type of the data entity property. The input type for the setter; the output type for the getter. |
Methods
Below is the list of methods.
Name | Description |
---|---|
Get | The method that is being invoked when the outbound transformation is triggered (i.e.: Query, QueryAll and BatchQuery). |
Set | The 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 is very useful when you would like to handle 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 can be unlimitted depends on your situation. In addition to this note, by implementing the property handler and mapping it to the property will ignore the automatic conversion of TypeMapper and enumerations.
How to Implement?
You have to manually 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
You can handle the property transformation on a property level. Imagine that you have a table named [dbo].[Person]
in which the column Address
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()
andSet()
of the property handler will be invoked immediately.
Type Level Handling
On the other hand, you can also handle the property transformation on a type level. It is useful on a situation if you would like to handle a specific database type transformation into a .NET CLR type (i.e.: converting the System.DateTime object Kind
to Utc
).
To enable this, you have to use the PropertyHandlerMapper class for the mappings.
Let us say, the scenario is to convert all the DateTime.Kind
properties to Utc in all read operations.
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);