/// <summary> /// Searches the type and child types for a field that matches the given column name. /// </summary> /// <param name="type">The type to search.</param> /// <param name="columnName">The column to search for.</param> /// <param name="mappingCollection">The mapping collection containing the configuration for this context.</param> /// <returns>The name of the field or null if there is no match.</returns> internal static string SearchForMatchingField(Type type, string columnName, MappingCollection mappingCollection) { var queue = new Queue <Tuple <Type, string> >(); queue.Enqueue(Tuple.Create(type, String.Empty)); var searched = new HashSet <Type>(); while (queue.Any()) { var tuple = queue.Dequeue(); var t = tuple.Item1; var prefix = tuple.Item2; searched.Add(t); var prop = GetMemberByColumnName(t, columnName); if (prop != null) { return(prefix + prop.Name); } if (mappingCollection.CanBindChild(t)) { foreach (var p in ClassPropInfo.GetMembersForType(t).Where(m => !TypeHelper.IsAtomicType(m.MemberType))) { if (!searched.Contains(p.MemberType)) { queue.Enqueue(Tuple.Create(p.MemberType, prefix + p.Name + MemberSeparator)); } } } } return(null); }
/// <summary> /// Uses IL to generate a method that converts an object of a given type to a FastExpando. /// </summary> /// <param name="type">The type of object to be able to convert.</param> /// <returns>A function that can convert that type of object to a FastExpando.</returns> private static Func <object, FastExpando> CreateConverter(Type type) { // create a dynamic method var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "ExpandoGenerator-{0}", type.FullName), typeof(FastExpando), new[] { typeof(object) }, typeof(ExpandoGenerator), true); var il = dm.GetILGenerator(); var source = il.DeclareLocal(type); // load the parameter object onto the stack and convert it into the local variable il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, type); il.Emit(OpCodes.Stloc, source); // new instance of fastexpando // top of stack il.Emit(OpCodes.Newobj, _constructor); // for each public field or method, get the value foreach (ClassPropInfo accessor in ClassPropInfo.GetMembersForType(type).Where(m => m.CanGetMember)) { il.Emit(OpCodes.Dup); // push expando - so we can call set value il.Emit(OpCodes.Ldstr, accessor.Name); // push name // get the value of the field or property il.Emit(OpCodes.Ldloc, source); accessor.EmitGetValue(il); // value types need to be boxed if (accessor.MemberType.GetTypeInfo().IsValueType) { il.Emit(OpCodes.Box, accessor.MemberType); } // call expando.setvalue il.Emit(OpCodes.Callvirt, _fastExpandoSetValue); } // return expando - it should be left on the stack il.Emit(OpCodes.Ret); return((Func <object, FastExpando>)dm.CreateDelegate(typeof(Func <object, FastExpando>))); }
/// <summary> /// Creates a deserializer for a graph of objects. /// </summary> /// <param name="subTypes">The types of the subobjects.</param> /// <param name="reader">The reader to analyze.</param> /// <param name="structure">The structure of the record we are reading.</param> /// <param name="allowBindChild">True if the columns should be allowed to bind to children.</param> /// <returns>A function that takes an IDataReader and deserializes an object of type T.</returns> private static Delegate CreateGraphDeserializer(Type[] subTypes, IDataReader reader, IRecordStructure structure, bool allowBindChild) { Type type = subTypes[0]; bool isStruct = type.GetTypeInfo().IsValueType; // go through each of the subtypes var deserializers = CreateDeserializersForSubObjects(subTypes, reader, structure, allowBindChild); // create a new anonymous method that takes an IDataReader and returns T var dm = new DynamicMethod( String.Format(CultureInfo.InvariantCulture, "Deserialize-{0}-{1}", type.FullName, Guid.NewGuid()), type, new[] { typeof(IDataReader) }, true); var il = dm.GetILGenerator(); var localObject = il.DeclareLocal(type); // keep track of the properties that we have already used // the tuple is the level + property var usedMethods = new HashSet <Tuple <int, ClassPropInfo> >(); /////////////////////////////////////////////////// // emit the method /////////////////////////////////////////////////// for (int i = 0; i < deserializers.Length; i++) { // if there is no deserializer for this object, then skip it if (deserializers[i] == null) { continue; } // for subobjects, dup the core object so we can set values on it if (i > 0) { if (isStruct) { il.Emit(OpCodes.Ldloca_S, localObject); } else { il.Emit(OpCodes.Ldloc, localObject); } } // if we don't have a callback, then we are going to store the value directly into the field on T or one of the subobjects // here we determine the proper set method to store into. // we are going to look into all of the types in the graph and find the first parameter that matches our current type ClassPropInfo setMethod = null; for (int parent = 0; parent < i; parent++) { // find the set method on the current parent setMethod = GetFirstMatchingMethod(ClassPropInfo.GetMembersForType(subTypes[parent]).Where(m => m.CanSetMember), subTypes[i]); // make sure that at a given level, we only use the method once var tuple = Tuple.Create(parent, setMethod); if (usedMethods.Contains(tuple)) { continue; } else { usedMethods.Add(tuple); } // if we didn't find a matching set method, then continue on to the next type in the graph if (setMethod == null) { continue; } // if the parent is not the root object, we have to drill down to the parent, then set the value // the root object is already on the stack, so emit a get method to get the object to drill down into for (int p = 0; p < parent; p++) { var getMethod = GetFirstMatchingMethod(ClassPropInfo.GetMembersForType(subTypes[p]).Where(m => m.CanGetMember && m.CanSetMember), subTypes[p + 1]); if (getMethod == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "In order to deserialize sub-objects, {0} must have a get/set method for type {1}", subTypes[p].FullName, subTypes[p + 1].FullName)); } getMethod.EmitGetValue(il); } break; } // call the deserializer for the subobject il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, deserializers[i]); // other than the root object, set the value on the parent object if (i == 0) { // store root object in loc.0 il.Emit(OpCodes.Stloc, localObject); } else { if (setMethod == null) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot find set method for type {0} into type {1}", subTypes[i].FullName, subTypes[0].FullName)); } setMethod.EmitSetValue(il); } } // return the object from loc.0 il.Emit(OpCodes.Ldloc, localObject); il.Emit(OpCodes.Ret); // convert the dynamic method to a delegate var delegateType = typeof(Func <,>).MakeGenericType(typeof(IDataReader), type); return(dm.CreateDelegate(delegateType)); }