// Create factory function that can convert a IDataReader record into a POCO public Delegate GetFactory(string sql, string connString, int firstColumn, int countColumns, IDataReader r) { // Check cache var key = Tuple.Create <string, string, int, int>(sql, connString, firstColumn, countColumns); return(PocoFactories.Get(key, () => { // Create the method var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), type, new Type[] { typeof(IDataReader) }, true); var il = m.GetILGenerator(); var mapper = Mappers.GetMapper(type); #if !PETAPOCO_NO_DYNAMIC if (type == typeof(object)) { il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj MethodInfo fnAdd = typeof(IDictionary <string, object>).GetMethod("Add"); // Enumerate all fields generating a set assignment for the column for (int i = firstColumn; i < firstColumn + countColumns; i++) { var srcType = r.GetFieldType(i); il.Emit(OpCodes.Dup); // obj, obj il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname // Get the converter Func <object, object> converter = mapper.GetFromDbConverter((PropertyInfo)null, srcType); /* * if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime)) * converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); }; */ // Setup stack for call to converter AddConverterToStack(il, converter); // r[i] il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value // Convert DBNull to null il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null) var lblNotNull = il.DefineLabel(); il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter? if (converter != null) { il.Emit(OpCodes.Pop); // obj, obj, fieldname, } il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null if (converter != null) { var lblReady = il.DefineLabel(); il.Emit(OpCodes.Br_S, lblReady); il.MarkLabel(lblNotNull); il.Emit(OpCodes.Callvirt, fnInvoke); il.MarkLabel(lblReady); } else { il.MarkLabel(lblNotNull); } il.Emit(OpCodes.Callvirt, fnAdd); } } else #endif if (type.IsValueType || type == typeof(string) || type == typeof(byte[])) { // Do we need to install a converter? var srcType = r.GetFieldType(0); var converter = GetConverter(mapper, null, srcType, type); // "if (!rdr.IsDBNull(i))" il.Emit(OpCodes.Ldarg_0); // rdr il.Emit(OpCodes.Ldc_I4_0); // rdr,0 il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool var lblCont = il.DefineLabel(); il.Emit(OpCodes.Brfalse_S, lblCont); il.Emit(OpCodes.Ldnull); // null var lblFin = il.DefineLabel(); il.Emit(OpCodes.Br_S, lblFin); il.MarkLabel(lblCont); // Setup stack for call to converter AddConverterToStack(il, converter); il.Emit(OpCodes.Ldarg_0); // rdr il.Emit(OpCodes.Ldc_I4_0); // rdr,0 il.Emit(OpCodes.Callvirt, fnGetValue); // value // Call the converter if (converter != null) { il.Emit(OpCodes.Callvirt, fnInvoke); } il.MarkLabel(lblFin); il.Emit(OpCodes.Unbox_Any, type); // value converted } else { // var poco=new T() il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null)); // Enumerate all fields generating a set assignment for the column for (int i = firstColumn; i < firstColumn + countColumns; i++) { // Get the PocoColumn for this db column, ignore if not known PocoColumn pc; if (!Columns.TryGetValue(r.GetName(i), out pc)) { continue; } // Get the source type for this column var srcType = r.GetFieldType(i); var dstType = pc.PropertyInfo.PropertyType; // "if (!rdr.IsDBNull(i))" il.Emit(OpCodes.Ldarg_0); // poco,rdr il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool var lblNext = il.DefineLabel(); il.Emit(OpCodes.Brtrue_S, lblNext); // poco il.Emit(OpCodes.Dup); // poco,poco // Do we need to install a converter? var converter = GetConverter(mapper, pc, srcType, dstType); // Fast bool Handled = false; if (converter == null) { var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) }); if (valuegetter != null && valuegetter.ReturnType == srcType && (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType))) { il.Emit(OpCodes.Ldarg_0); // *,rdr il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i il.Emit(OpCodes.Callvirt, valuegetter); // *,value // Convert to Nullable if (Nullable.GetUnderlyingType(dstType) != null) { il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) })); } il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco Handled = true; } } // Not so fast if (!Handled) { // Setup stack for call to converter AddConverterToStack(il, converter); // "value = rdr.GetValue(i)" il.Emit(OpCodes.Ldarg_0); // *,rdr il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i il.Emit(OpCodes.Callvirt, fnGetValue); // *,value // Call the converter if (converter != null) { il.Emit(OpCodes.Callvirt, fnInvoke); } // Assign it il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco } il.MarkLabel(lblNext); } var fnOnLoaded = RecurseInheritedTypes <MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null)); if (fnOnLoaded != null) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Callvirt, fnOnLoaded); } } il.Emit(OpCodes.Ret); // Cache it, return it return m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type)); } )); }
// Create factory function that can convert a IDataReader record into a POCO public Delegate GetFactory(string sql, string connString, bool ForceDateTimesToUtc, int firstColumn, int countColumns, IDataReader r) { var key = string.Format("{0}:{1}:{2}:{3}:{4}", sql, connString, ForceDateTimesToUtc, firstColumn, countColumns).GetHashCode().ToString(); RWLock.EnterReadLock(); try { // Have we already created it? Delegate factory; if (PocoFactories.TryGetValue(key, out factory)) { return(factory); } } finally { RWLock.ExitReadLock(); } // Take the writer lock RWLock.EnterWriteLock(); try { // Check again, just in case Delegate factory; if (PocoFactories.TryGetValue(key, out factory)) { return(factory); } PocoFactories.Clear(); // Create the method var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), type, new Type[] { typeof(IDataReader) }, true); var il = m.GetILGenerator(); //#if !PETAPOCO_NO_DYNAMIC // if (type == typeof(object)) // { // // var poco=new T() // il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes)); // obj // MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add"); // // Enumerate all fields generating a set assignment for the column // for (int i = firstColumn; i < firstColumn + countColumns; i++) // { // var srcType = r.GetFieldType(i); // il.Emit(OpCodes.Dup); // obj, obj // il.Emit(OpCodes.Ldstr, r.GetName(i)); // obj, obj, fieldname // // Get the converter // Func<object, object> converter = null; // if (Database.Mapper != null) // converter = Database.Mapper.GetFromDbConverter(null, srcType); // if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime)) // converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); }; // // Setup stack for call to converter // AddConverterToStack(il, converter); // // r[i] // il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?, rdr // il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?, rdr,i // il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?, value // // Convert DBNull to null // il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?, value, value // il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?, value, (value or null) // var lblNotNull = il.DefineLabel(); // il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?, value // il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter? // if (converter != null) // il.Emit(OpCodes.Pop); // obj, obj, fieldname, // il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null // if (converter != null) // { // var lblReady = il.DefineLabel(); // il.Emit(OpCodes.Br_S, lblReady); // il.MarkLabel(lblNotNull); // il.Emit(OpCodes.Callvirt, fnInvoke); // il.MarkLabel(lblReady); // } // else // { // il.MarkLabel(lblNotNull); // } // il.Emit(OpCodes.Callvirt, fnAdd); // } // } // else //#endif if (type.IsValueType || type == typeof(string) || type == typeof(byte[])) { // Do we need to install a converter? var srcType = r.GetFieldType(0); var converter = GetConverter(ForceDateTimesToUtc, null, srcType, type); // "if (!rdr.IsDBNull(i))" il.Emit(OpCodes.Ldarg_0); // rdr il.Emit(OpCodes.Ldc_I4_0); // rdr,0 il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool var lblCont = il.DefineLabel(); il.Emit(OpCodes.Brfalse_S, lblCont); il.Emit(OpCodes.Ldnull); // null var lblFin = il.DefineLabel(); il.Emit(OpCodes.Br_S, lblFin); il.MarkLabel(lblCont); // Setup stack for call to converter AddConverterToStack(il, converter); il.Emit(OpCodes.Ldarg_0); // rdr il.Emit(OpCodes.Ldc_I4_0); // rdr,0 il.Emit(OpCodes.Callvirt, fnGetValue); // value // Call the converter if (converter != null) { il.Emit(OpCodes.Callvirt, fnInvoke); } il.MarkLabel(lblFin); il.Emit(OpCodes.Unbox_Any, type); // value converted } else { // var poco=new T() il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null)); // Enumerate all fields generating a set assignment for the column for (int i = firstColumn; i < firstColumn + countColumns; i++) { // Get the PocoColumn for this db column, ignore if not known PocoColumn pc; if (!Columns.TryGetValue(r.GetName(i), out pc)) { continue; } // Get the source type for this column var srcType = r.GetFieldType(i); var dstType = pc.PropertyInfo.PropertyType; // "if (!rdr.IsDBNull(i))" il.Emit(OpCodes.Ldarg_0); // poco,rdr il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool var lblNext = il.DefineLabel(); il.Emit(OpCodes.Brtrue_S, lblNext); // poco il.Emit(OpCodes.Dup); // poco,poco // Do we need to install a converter? var converter = GetConverter(ForceDateTimesToUtc, pc, srcType, dstType); // Fast bool Handled = false; if (converter == null) { var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) }); if (valuegetter != null && valuegetter.ReturnType == srcType && (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType))) { il.Emit(OpCodes.Ldarg_0); // *,rdr il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i il.Emit(OpCodes.Callvirt, valuegetter); // *,value // Convert to Nullable if (Nullable.GetUnderlyingType(dstType) != null) { il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) })); } il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco Handled = true; } } // Not so fast if (!Handled) { // Setup stack for call to converter AddConverterToStack(il, converter); // "value = rdr.GetValue(i)" il.Emit(OpCodes.Ldarg_0); // *,rdr il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i il.Emit(OpCodes.Callvirt, fnGetValue); // *,value // Call the converter if (converter != null) { il.Emit(OpCodes.Callvirt, fnInvoke); } // Assign it il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco } il.MarkLabel(lblNext); } var fnOnLoaded = RecurseInheritedTypes <MethodInfo>(type, (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null)); if (fnOnLoaded != null) { il.Emit(OpCodes.Dup); il.Emit(OpCodes.Callvirt, fnOnLoaded); } } il.Emit(OpCodes.Ret); // Cache it, return it var del = m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), type)); PocoFactories.Add(key, del); return(del); } finally { RWLock.ExitWriteLock(); } }