public class EntityMapper { private delegate T mapEntity<T>(DbDataReader dr); private static Dictionary<Type, Delegate> cachedMappers = new Dictionary<Type, Delegate>(); public static IEnumerable<T> MapToEntities<T>(DbDataReader dr) { // If a mapping function from dr -> T does not exist, create and cache one if (!cachedMappers.ContainsKey(typeof(T))) { // Our method will take in a single parameter, a DbDataReader Type[] methodArgs = { typeof(DbDataReader) }; // The MapDR method will map a DbDataReader row to an instance of type T DynamicMethod dm = new DynamicMethod("MapDR", typeof(T), methodArgs, Assembly.GetExecutingAssembly().GetType().Module); ILGenerator il = dm.GetILGenerator(); // We'll have a single local variable, the instance of T we're mapping il.DeclareLocal(typeof(T)); // Create a new instance of T and save it as variable 0 il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes)); il.Emit(OpCodes.Stloc_0); foreach (PropertyInfo pi in typeof(T).GetProperties()) { // Load the T instance, SqlDataReader parameter and the field name onto the stack il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, pi.Name); // Push the column value onto the stack il.Emit(OpCodes.Callvirt, typeof(DbDataReader).GetMethod("get_Item", new Type[] { typeof(string) })); // Depending on the type of the property, convert the datareader column value to the type switch (pi.PropertyType.Name) { case "Int16": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt16", new Type[] { typeof(object) })); break; case "Int32": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt32", new Type[] { typeof(object) })); break; case "Int64": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToInt64", new Type[] { typeof(object) })); break; case "Boolean": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToBoolean", new Type[] { typeof(object) })); break; case "String": il.Emit(OpCodes.Callvirt, typeof(string).GetMethod("ToString", new Type[] { })); break; case "DateTime": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDateTime", new Type[] { typeof(object) })); break; case "Decimal": il.Emit(OpCodes.Call, typeof(Convert).GetMethod("ToDecimal", new Type[] { typeof(object) })); break; default: // Don't set the field value as it's an unsupported type continue; } // Set the T instances property value il.Emit(OpCodes.Callvirt, typeof(T).GetMethod("set_" + pi.Name, new Type[] { pi.PropertyType })); } // Load the T instance onto the stack il.Emit(OpCodes.Ldloc_0); // Return il.Emit(OpCodes.Ret); // Cache the method so we won't have to create it again for the type T cachedMappers.Add(typeof(T), dm.CreateDelegate(typeof(mapEntity<T>))); } // Get a delegate reference to the dynamic method mapEntity<T> invokeMapEntity = (mapEntity<T>)cachedMappers[typeof(T)]; // For each row, map the row to an instance of T and yield return it while (dr.Read()) yield return invokeMapEntity(dr); } public static void ClearCachedMapperMethods() { cachedMappers.Clear(); } }
2016 © donet5.comApache Licence 2.0