From 236a948d2cc99e7f731c5b3e38d5fe1e527733f2 Mon Sep 17 00:00:00 2001 From: mgravell Date: Wed, 1 Jun 2011 11:38:57 +0100 Subject: [PATCH] parse enums from string, ala L2S --- Dapper/SqlMapper.cs | 56 +++++++++++++++++++++++++++++++++++++++++---- Tests/Tests.cs | 22 ++++++++++++++++-- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Dapper/SqlMapper.cs b/Dapper/SqlMapper.cs index 85af6f7..aea878e 100644 --- a/Dapper/SqlMapper.cs +++ b/Dapper/SqlMapper.cs @@ -849,6 +849,11 @@ private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, return (T)val; }; } + static readonly MethodInfo + enumParse = typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string), typeof(bool) }), + getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public) + .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int)) + .Select(p => p.GetGetMethod()).First(); public static Func GetClassDeserializer( #if CSHARP30 @@ -863,6 +868,7 @@ private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, var il = dm.GetILGenerator(); il.DeclareLocal(typeof(int)); il.DeclareLocal(typeof(T)); + bool haveEnumLocal = false; il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_0); var properties = typeof(T) @@ -896,11 +902,6 @@ private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, select new { Name = n, Property = prop, Field = field } ).ToList(); - - var getItem = typeof(IDataRecord).GetProperties(BindingFlags.Instance | BindingFlags.Public) - .Where(p => p.GetIndexParameters().Any() && p.GetIndexParameters()[0].ParameterType == typeof(int)) - .Select(p => p.GetGetMethod()).First(); - int index = startBound; il.BeginExceptionBlock(); @@ -922,6 +923,7 @@ private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index] il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object] + il.Emit(OpCodes.Dup); // stack is now [target][target][value][value] il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null] il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object] @@ -930,6 +932,50 @@ private static int ExecuteCommand(IDbConnection cnn, IDbTransaction tranaction, Type memberType = item.Property != null ? item.Property.Type : item.Field.FieldType; var nullUnderlyingType = Nullable.GetUnderlyingType(memberType); var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType; + + if (unboxType.IsEnum) + { + if (!haveEnumLocal) + { + il.DeclareLocal(typeof(string)); + haveEnumLocal = true; + } + + Label isNotString = il.DefineLabel(); + il.Emit(OpCodes.Dup); // stack is now [target][target][value][value] + il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null] + il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null] + il.Emit(OpCodes.Stloc_2); // stack is now [target][target][value-as-object][string or null] + il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object] + + il.Emit(OpCodes.Pop); // stack is now [target][target] + + + il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token] + il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type] + il.Emit(OpCodes.Ldloc_2); // stack is now [target][target][enum-type][string] + il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true] + il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object] + + il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value] + + if (nullUnderlyingType != null) + { + il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); + } + 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); + + + il.MarkLabel(isNotString); + } il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value] if (nullUnderlyingType != null && nullUnderlyingType.IsEnum) { diff --git a/Tests/Tests.cs b/Tests/Tests.cs index 15683fb..cc6275d 100644 --- a/Tests/Tests.cs +++ b/Tests/Tests.cs @@ -528,10 +528,28 @@ class TestEnumClass { public TestEnum? EnumEnum { get; set; } } + class TestEnumClassNoNull + { + public TestEnum EnumEnum { get; set; } + } public void TestEnumWeirdness() { - connection.Query("select null as [EnumEnum]"); - connection.Query("select cast(1 as tinyint) as [EnumEnum]"); + connection.Query("select null as [EnumEnum]").First().EnumEnum.IsEqualTo(null); + connection.Query("select cast(1 as tinyint) as [EnumEnum]").First().EnumEnum.IsEqualTo(TestEnum.Bla); + } + void Foo() + { + string s = "Bla"; + var obj = new TestEnumClassNoNull(); + obj.EnumEnum = (TestEnum)Enum.Parse(typeof(TestEnum), s, true); + } + public void TestEnumStrings() + { + connection.Query("select 'BLA' as [EnumEnum]").First().EnumEnum.IsEqualTo(TestEnum.Bla); + connection.Query("select 'bla' as [EnumEnum]").First().EnumEnum.IsEqualTo(TestEnum.Bla); + + connection.Query("select 'BLA' as [EnumEnum]").First().EnumEnum.IsEqualTo(TestEnum.Bla); + connection.Query("select 'bla' as [EnumEnum]").First().EnumEnum.IsEqualTo(TestEnum.Bla); } public void TestSupportForParamDictionary() -- GitLab