Dashboard > iBATIS DataMapper > Home > Frequently Asked Questions > How do I use a Custom Type Handler? cs
How do I use a Custom Type Handler? cs
Added by Roberto Rabe, last edited by Roberto Rabe on May 22, 2005  (view change)
Labels: 
(None)


If you find yourself bumping into a type conversion issue when moving data between a class property and your database, a custom type handler may be just what you're looking for...

A custom type handler allows you to extend the DataMapper's capabilities in handling types that are specific to your database provider, not handled by your database provider, or just happen to be part of your application design. The .NET DataMapper provides an interface, IBatisNet.DataMappers.TypeHandlers.ITypeHandlerCallback, for you to use in implementing your custom type handler.

"ITypeHandlerCallback"
using System.Data;
    using IBatisNet.DataMapper.Configuration.ParameterMapping;
    
    
    namespace IBatisNet.DataMapper.TypeHandlers
    {
    public interface ITypeHandlerCallback
    {
    void SetParameter(IParameterSetter setter, object parameter);
    
    object GetResult(IResultGetter getter);
    
    object ValueOf(string s);
    }
    }

The SetParameter method allows you to process a <statement> parameter's value before it is added as an IDbCommand parameter. This enables you to do any necessary type conversion and clean-up before the DataMapper gets to work. If needed, you also have access to the underlying IDataParameter through the setter.DataParameter property.

The GetResult method allows you to process a database result value right after it has been retrieved by the DataMapper and before it is used in your resultClass, resultMap, or listClass. If needed, you also have access to the underlying IDataReader through the getter.DataReader property.

The ValueOf method allows you to compare a string representation of a value with one that you are expecting and can handle appropriately. Typically, this is useful for translating a null value, but if your application or database will not support a null value, you can basically return the given string. When presented with an unexpected value, you can throw an appropriate exception.

One scenario that is familiar to .NET developers is the handling of a Guid type/structure. Many providers do not handle Guid class properties well, and developers may be faced with littering their domain objects with an additional set of property accessors to translate Guid properties into strings and strings into Guids.

public class BudgetObjectCode
    {
    private string _code;
    private string _description;
    private Guid _guidProperty;
    
    ...
    
    public Guid GuidProperty	{
    get { return _guidProperty; }
    set { __guidProperty = value; }
    }
    
    public string GuidPropertyString {
    get { return _guidProperty.ToString(); }
    set {
    if (value == null) {
    _guidProperty = Guid.Empty;
    }
    else {
    _guidProperty = new Guid(value.ToString());
    }
    }
    }
    
    ...
    }

We can use a custom type handler to clean up this domain class. First, we define a string that will represent a null Guid value (Guid.Empty). We can then use that constant in our ValueOf null value comparison for the DataMapper to eventually use in setting our domain class' Guid properties. Implementing the GetResult and SetParameter methods is straightforward since we had been basically doing the same translation in our domain class' GuidPropertyString accessors.

Guid String Type Handler
using System;
    using IBatisNet.DataMapper.TypeHandlers;
    
    namespace BigApp.Common.TypeHandlers
    {
    /// <summary>
    	/// GuidVarcharTypeHandlerCallback.
    	/// </summary>
    	public class GuidVarcharTypeHandlerCallback : ITypeHandlerCallback
    {
    private const string GUIDNULL = "00000000-0000-0000-0000-000000000000";
    
    public object ValueOf(string nullValue)
    {
    if (GUIDNULL.Equals(nullValue))
    {
    return Guid.Empty;
    }
    else
    {
    throw new Exception(
    "Unexpected value " + nullValue +
    " found where "+GUIDNULL+" was expected to represent a null value.");
    }
    }
    
    public object GetResult(IResultGetter getter)
    {
    try {
    Guid result = new Guid(getter.Value.ToString());
    return result;
    }
    catch
    {
    throw new Exception(
    "Unexpected value " + getter.Value.ToString() +
    " found where a valid GUID string value was expected.");
    }
    }
    
    public void SetParameter(IParameterSetter setter, object parameter)
    {
    setter.Value = parameter.ToString();
    }
    
    }
    }

Now that we have our custom type handler, we can clean up our domain class and use the handler in our SqlMaps. To do that, we have two options in configuring our custom type handler to be used by the DataMapper. We can simply add it as a <typeAlias> and use it when needed in a parameterMap or resultMap.

Aliased Custom Type Handler in a SqlMap.xml file
<alias>
    <typeAlias alias="GuidVarchar"
    type="BigApp.Common.TypeHandlers.GuidVarcharTypeHandlerCallback,
    BigApp.Common"/>
    </alias>
    
    <resultMaps>
    <resultMap id="boc-result"  class="BudgetObjectCode">
    <result property="Code" column="BOC_CODE" dbType="Varchar2"/>
    <result property="Description" column="BOC_DESC" dbType="Varchar2"/>
    <result property="GuidProperty" column="BOC_GUID" typeHandler="GuidVarchar"/>
    </resultMap>
    </resultMaps>

Or we can specify it as a basic <typeHandler> for all Guid types mapped in our SqlMap files.

<typeHandler> in SqlMap.config
[Our SqlMap.config]
    <alias>
    <typeAlias alias="GuidVarchar"
    type="BigApp.Common.TypeHandlers.GuidVarcharTypeHandlerCallback,
    BigApp.Common"/>
    </alias>
    
    <typeHandlers>
    <typeHandler type="guid" dbType="Varchar2" callback="GuidVarchar"/>
    </typeHandlers>
    
    
    [One of our SqlMap.xml files]
    <parameterMaps>
    <parameterMap id="boc-params">
    <parameter property="Code" dbType="Varchar2" size="10"/>
    <parameter property="Description" dbType="Varchar2" size="100"/>
    <parameter property="GuidProperty" dbType="Varchar2" type="guid"/>
    </parameterMap>
    </parameterMaps>
    
    <resultMaps>
    <resultMap id="boc-result"  class="BudgetObjectCode">
    <result property="Code" column="BOC_CODE" dbType="Varchar2"/>
    <result property="Description" column="BOC_DESC" dbType="Varchar2"/>
    <result property="GuidProperty" column="BOC_GUID" dbType="Varchar2" type="guid"/>
    </resultMap>
    </resultMaps>

Site running on a free Atlassian Confluence Open Source Project License granted to OSS. Evaluate Confluence today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.5.5 Build:#811 Jul 25, 2007) - Bug/feature request - Contact Administrators