Commit 33c5976a authored by Marc Gravell's avatar Marc Gravell

For value types, the nullable and non-nullable type handler should be in sync (via chilversc)

parent 5c633e85
......@@ -632,6 +632,15 @@ static SqlMapper()
AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), false);
}
/// <summary>
/// Clear the registered type handlers
/// </summary>
public static void ResetTypeHandlers()
{
typeHandlers = new Dictionary<Type, ITypeHandler>();
AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), true);
}
/// <summary>
/// Configire the specified type to be mapped to a given db-type
/// </summary>
......@@ -663,6 +672,22 @@ public static void AddTypeHandlerImpl(Type type, ITypeHandler handler, bool clon
{
if (type == null) throw new ArgumentNullException("type");
Type secondary = null;
if(type.IsValueType)
{
var underlying = Nullable.GetUnderlyingType(type);
if(underlying == null)
{
secondary = typeof(Nullable<>).MakeGenericType(type); // the Nullable<T>
// type is already the T
}
else
{
secondary = type; // the Nullable<T>
type = underlying; // the T
}
}
var snapshot = typeHandlers;
ITypeHandler oldValue;
if (snapshot.TryGetValue(type, out oldValue) && handler == oldValue) return; // nothing to do
......@@ -671,9 +696,21 @@ public static void AddTypeHandlerImpl(Type type, ITypeHandler handler, bool clon
#pragma warning disable 618
typeof(TypeHandlerCache<>).MakeGenericType(type).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
if(secondary != null)
{
typeof(TypeHandlerCache<>).MakeGenericType(secondary).GetMethod("SetHandler", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { handler });
}
#pragma warning restore 618
if (handler == null) newCopy.Remove(type);
else newCopy[type] = handler;
if (handler == null)
{
newCopy.Remove(type);
if (secondary != null) newCopy.Remove(secondary);
}
else
{
newCopy[type] = handler;
if(secondary != null) newCopy[secondary] = handler;
}
typeHandlers = newCopy;
}
......
......@@ -3230,6 +3230,60 @@ public enum Status : byte
}
public void Issue136_ValueTypeHandlers()
{
Dapper.SqlMapper.ResetTypeHandlers();
Dapper.SqlMapper.AddTypeHandler(typeof(LocalDate), LocalDateHandler.Default);
var param = new LocalDateResult
{
NotNullable = new LocalDate { Year = 2014, Month = 7, Day = 25 },
NullableNotNull = new LocalDate { Year = 2014, Month = 7, Day = 26 },
NullableIsNull = null,
};
var result = connection.Query<LocalDateResult>("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single();
Dapper.SqlMapper.ResetTypeHandlers();
Dapper.SqlMapper.AddTypeHandler(typeof(LocalDate?), LocalDateHandler.Default);
result = connection.Query<LocalDateResult>("SELECT @NotNullable AS NotNullable, @NullableNotNull AS NullableNotNull, @NullableIsNull AS NullableIsNull", param).Single();
}
public class LocalDateHandler : Dapper.SqlMapper.TypeHandler<LocalDate>
{
private LocalDateHandler() { }
// Make the field type ITypeHandler to ensure it cannot be used with SqlMapper.AddTypeHandler<T>(TypeHandler<T>)
// by mistake.
public static readonly Dapper.SqlMapper.ITypeHandler Default = new LocalDateHandler();
public override LocalDate Parse(object value)
{
var date = (DateTime)value;
return new LocalDate { Year = date.Year, Month = date.Month, Day = date.Day };
}
public override void SetValue(IDbDataParameter parameter, LocalDate value)
{
parameter.DbType = DbType.DateTime;
parameter.Value = new DateTime(value.Year, value.Month, value.Day);
}
}
public struct LocalDate
{
public int Year { get; set; }
public int Month { get; set; }
public int Day { get; set; }
}
public class LocalDateResult
{
public LocalDate NotNullable { get; set; }
public LocalDate? NullableNotNull { get; set; }
public LocalDate? NullableIsNull { get; set; }
}
#if POSTGRESQL
class Cat
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment