From b73acfcecf13343eebc8e5aa0a48e596eb0f5938 Mon Sep 17 00:00:00 2001 From: vosen Date: Fri, 2 Mar 2012 15:37:15 +0100 Subject: [PATCH] Add support for types with constructors matching the types pulled by data reader. --- Dapper/SqlMapper.cs | 90 +++++++++++++++++++++++++++++++++------------ Tests/Tests.cs | 18 +++------ 2 files changed, 71 insertions(+), 37 deletions(-) diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 809be5d..b9cd8a2 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -1497,6 +1497,7 @@ static List GetSettableFields(Type t) int index = startBound; + ConstructorInfo specializedConstructor = null; if (type.IsValueType) { il.Emit(OpCodes.Ldloca_S, (byte)1); @@ -1504,19 +1505,39 @@ static List GetSettableFields(Type t) } else { - var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); - if (ctor == null) + var types = new Type[length - startBound]; + for (int i = startBound; i < startBound + length; i++) { - throw new InvalidOperationException("A parameterless default constructor is required to allow for dapper materialization"); + types[i - startBound] = reader.GetFieldType(i); + } + var ctorWithParameters = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null); + if (ctorWithParameters != null) + { + var ctorParams = ctorWithParameters.GetParameters(); + for(int i =0; i< ctorParams.Length; i++) + { + if (!String.Equals(ctorParams[i].Name, names[i], StringComparison.OrdinalIgnoreCase)) + break; + specializedConstructor = ctorWithParameters; + } + } + if(specializedConstructor == null) + { + var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); + if (ctor == null) + { + throw new InvalidOperationException("A parameterless default constructor is required to allow for dapper materialization"); + } + il.Emit(OpCodes.Newobj, ctor); + il.Emit(OpCodes.Stloc_1); } - il.Emit(OpCodes.Newobj, ctor); - il.Emit(OpCodes.Stloc_1); } il.BeginExceptionBlock(); if(type.IsValueType) { il.Emit(OpCodes.Ldloca_S, (byte)1);// [target] - } else + } + else if(specializedConstructor == null) { il.Emit(OpCodes.Ldloc_1);// [target] } @@ -1529,7 +1550,8 @@ static List GetSettableFields(Type t) { if (item.Property != null || item.Field != null) { - il.Emit(OpCodes.Dup); // stack is now [target][target] + if(specializedConstructor == null) + il.Emit(OpCodes.Dup); // stack is now [target][target] Label isDbNullLabel = il.DefineLabel(); Label finishLabel = il.DefineLabel(); @@ -1587,13 +1609,16 @@ static List GetSettableFields(Type t) { il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); } - if (item.Property != null) - { - il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target] - } - else + if (specializedConstructor == null) { - il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] + if (item.Property != null) + { + il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target] + } + else + { + il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] + } } il.Emit(OpCodes.Br_S, finishLabel); @@ -1614,28 +1639,41 @@ static List GetSettableFields(Type t) il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); } } - if (item.Property != null) + if (specializedConstructor == null) { - if (type.IsValueType) + if (item.Property != null) { - il.Emit(OpCodes.Call, item.Property.Setter); // stack is now [target] + if (type.IsValueType) + { + il.Emit(OpCodes.Call, item.Property.Setter); // stack is now [target] + } + else + { + il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target] + } } else { - il.Emit(OpCodes.Callvirt, item.Property.Setter); // stack is now [target] + il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] } } - else - { - il.Emit(OpCodes.Stfld, item.Field); // stack is now [target] - } il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target] il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value] - - il.Emit(OpCodes.Pop); // stack is now [target][target] - il.Emit(OpCodes.Pop); // stack is now [target] + if (specializedConstructor != null) + { + Type itemType = item.Property != null ? item.Property.Type : item.Field.FieldType; + if (itemType.IsValueType) + il.Emit(OpCodes.Initobj, itemType); + else + il.Emit(OpCodes.Ldnull); + } + else + { + il.Emit(OpCodes.Pop); // stack is now [target][target] + il.Emit(OpCodes.Pop); // stack is now [target] + } if (first && returnNullIfFirstMissing) { @@ -1656,6 +1694,10 @@ static List GetSettableFields(Type t) } else { + if (specializedConstructor != null) + { + il.Emit(OpCodes.Newobj, specializedConstructor); + } il.Emit(OpCodes.Stloc_1); // stack is empty } il.MarkLabel(allDone); diff --git a/Tests/Tests.cs b/Tests/Tests.cs index 825cd23..9a9c2ec 100644 --- a/Tests/Tests.cs +++ b/Tests/Tests.cs @@ -33,29 +33,21 @@ public class ConcreteOrder : Order } } - class NoDefualtConstructor + class NoDefaultConstructor { - public NoDefualtConstructor(int a) + public NoDefaultConstructor(int a) { A = a; } public int A { get; set; } } - public void EnsureNoConstructorGivesNiceError() + public void TestNoDefaultConstructor() { - try - { - connection.Query("select 1 A").First(); - } - catch(InvalidOperationException e) - { - e.Message.IsEqualTo("A parameterless default constructor is required to allow for dapper materialization"); - } - + NoDefaultConstructor nodef = connection.Query("select 1 A").First(); + nodef.A.IsEqualTo(1); } - // http://stackoverflow.com/q/8593871 public void TestAbstractInheritance() { -- GitLab