I'm trying to sidestep the Decimal .net type that Oracle creates. I have to work with a table from which i create reverse-engineered entities, entities which i have to work on with NHibernate. (it starts here: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=20247)
I decided to use FluentNhibernate for the mappings, and since FluentNhibernate doesn't like entities keys that are not integral, i went with a typeconverter to convert the decimals to long (int64)
I'm working with LLBLGEN Pro v3.1 on .net4. I created an assembly that i compile against .net4 which contains the following code:
using System;
using System.ComponentModel;
using NHibernate.SqlTypes;
using NHibernate.UserTypes;
using System.Data;
using NHibernate;
namespace WorkArea
{
public class IntegralDecimalConverterNHibernate : TypeConverter, IUserType
{
public IntegralDecimalConverterNHibernate()
{
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
switch(sourceType.FullName)
{
case "System.Decimal":
return true;
default:
return false;
}
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
switch(destinationType.FullName)
{
case "System.Decimal":
return true;
default:
return false;
}
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
Int64 toReturn = 0;
switch(value.GetType().FullName)
{
case "System.Decimal":
toReturn = ((Int64)value);
break;
default:
throw new NotSupportedException("Conversion from a value of type '" + value.GetType().ToString() + "' to System.Int64 isn't supported");
}
return toReturn;
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if(value==null)
{
throw new ArgumentNullException("value", "Value can't be null");
}
if(! (value is Int64))
{
throw new ArgumentException("Value isn't of type int64", "value");
}
switch(destinationType.FullName)
{
case "System.Decimal":
return (decimal)value;
default:
throw new NotSupportedException("Conversion to a value of type '" + destinationType.ToString() + "' isn't supported");
}
}
public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues)
{
return ((Int64)0);
}
#region IUserType Members
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public int GetHashCode(object x)
{
return x == null? base.GetHashCode() : x.GetHashCode();
}
public bool IsMutable
{
get { return true; }
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var rawValue = NHibernateUtil.Decimal.NullSafeGet(rs, names[0]);
if(rawValue==null)
{
return null;
}
return ConvertFrom(null, null, rawValue);
}
public void NullSafeSet(System.Data.IDbCommand cmd, object value, int index)
{
((IDataParameter)cmd.Parameters[index]).Value = value == null ? DBNull.Value : ConvertTo(null, null, value, typeof(Decimal));
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType
{
get { return typeof(Int64); }
}
public NHibernate.SqlTypes.SqlType[] SqlTypes
{
get
{
return new SqlType[] { NHibernateUtil.Decimal.SqlType};
}
}
bool IUserType.Equals(object x, object y)
{
if(ReferenceEquals(x, y))
{
return true;
}
return (x != null && y != null) && x.Equals(y);
}
#endregion
}
}
The code compiles, i copy the typeconverter to LLBLGEN typeconverter directory, and i can map my fields to the typeconverter to match the Oracle Decimal field. However, when i generate the code, the fluent mappings aren't created with the TypeConverter i defined.
However, if i apply the typeconverter to a field that is not a entity key, the mapping is created with the correct typeconverter. Is there a configuration parameter somewhere to force the use of the type converter on entity keys?