예제 #1
0
        /// <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);
        }
예제 #2
0
		/// <summary>
		/// Initializes a new instance of the FieldMapping class.
		/// </summary>
		/// <param name="pathToMember">The path to the member.</param>
		/// <param name="member">The member that is bound.</param>
		/// <param name="serializer">The serializer for the mapping.</param>
		public FieldMapping(string pathToMember, ClassPropInfo member, IDbObjectSerializer serializer)
		{
			PathToMember = pathToMember;
			Member = member;
			Serializer = serializer;
			Prefix = ClassPropInfo.GetMemberPrefix(pathToMember);
			IsDeep = (Prefix != null);
		}
예제 #3
0
        /// <summary>
        /// Emits the code to get a value from a type, allowing dot accessors to select children.
        /// </summary>
        /// <param name="type">The base type.</param>
        /// <param name="memberName">The name of the member in the form a.b.c.d.</param>
        /// <param name="il">The ILGenerator to use,</param>
        /// <param name="readyToSetLabel">The label to jump to if any of the non-terminal accesses result (a.b.c) in null.</param>
        internal static void EmitGetValue(Type type, string memberName, ILGenerator il, Label?readyToSetLabel = null)
        {
            ClassPropInfo pi          = null;
            var           memberNames = SplitMemberName(memberName).ToArray();

            Label nullLabel = il.DefineLabel();

            // emit the code to get the value
            for (int i = 0; i < memberNames.Length; i++)
            {
                var member = memberNames[i];

                // if the value on the stack can be null, do a null check here
                if (pi != null && !pi.MemberType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Brfalse, nullLabel);
                }

                pi = FindMember(type, member);
                if (pi == null || !pi.CanGetMember)
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Get method {0} not found on {1}", member, type));
                }

                pi.EmitGetValue(il);

                type = pi.MemberType;
            }

            // emit the code to handle nulls
            var memberType = pi.MemberType;

            if (memberNames.Length > 1)
            {
                var doneLabel = il.DefineLabel();
                il.Emit(OpCodes.Br, doneLabel);

                // if any of the prefix values are null, then we emit the default of the final type
                il.MarkLabel(nullLabel);
                il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ldnull);
                if (readyToSetLabel != null)
                {
                    il.Emit(OpCodes.Br, readyToSetLabel.Value);
                }

                il.MarkLabel(doneLabel);
            }

            // if this is a value type, then box the value so the compiler can check the type and we can call methods on it
            if (memberType.GetTypeInfo().IsValueType)
            {
                il.Emit(OpCodes.Box, memberType);
            }
        }
예제 #4
0
        /// <summary>
        /// Splits a member name on dots and returns the prefix to the member.
        /// </summary>
        /// <param name="memberName">The name of the member.</param>
        /// <returns>The prefix.</returns>
        internal static string GetMemberPrefix(string memberName)
        {
            var split = ClassPropInfo.SplitMemberName(memberName);

            if (split.Count() < 2)
            {
                return(null);
            }

            return(String.Join(ClassPropInfo.MemberSeparator, split.Take(split.Count() - 1).ToArray()));
        }
예제 #5
0
        /// <summary>
        /// Gets the member from a type, allowing dot accessors to select children.
        /// </summary>
        /// <param name="type">The type to search.</param>
        /// <param name="memberName">The name of the member in the form a.b.c.d.</param>
        /// <returns>The member or null if not found.</returns>
        internal static ClassPropInfo FindMember(Type type, string memberName)
        {
            ClassPropInfo pi = null;

            foreach (var member in SplitMemberName(memberName))
            {
                pi = GetMemberByName(type, member);
                if (pi == null)
                {
                    return(null);
                }

                type = pi.MemberType;
            }

            return(pi);
        }
예제 #6
0
        /// <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>)));
        }
예제 #7
0
        /// <summary>
        /// Generates an accessor for a property in the object.
        /// </summary>
        /// <param name="type">The type of object being read.</param>
        /// <param name="mapping">The column mapping.</param>
        /// <param name="columnInfo">The column information.</param>
        /// <returns>An accessor function or null if there is no mapping.</returns>
        private static Func <object, object> GenerateAccessor(Type type, FieldMapping mapping, ColumnInfo columnInfo)
        {
            if (mapping == null)
            {
                return(null);
            }

            ClassPropInfo propInfo = mapping.Member;

            // create a new anonymous method that takes an object and returns the value
            var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true);
            var il = dm.GetILGenerator();

            // convert the object reference to the desired type
            il.Emit(OpCodes.Ldarg_0);
            if (type.IsValueType)
            {
                // access the field/property of a value type
                var valueHolder = il.DeclareLocal(type);
                il.Emit(OpCodes.Unbox_Any, type);
                il.Emit(OpCodes.Stloc, valueHolder);
                il.Emit(OpCodes.Ldloca_S, valueHolder);
            }
            else
            {
                il.Emit(OpCodes.Isinst, type);                                  // cast object -> type
            }
            // get the value from the object
            var readyToSetLabel = il.DefineLabel();

            ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel);
            if (mapping.Member.MemberType.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, mapping.Member.MemberType);
            }

            // if the type is nullable, handle nulls
            Type sourceType     = propInfo.MemberType;
            Type targetType     = columnInfo.ColumnType;
            Type underlyingType = Nullable.GetUnderlyingType(sourceType);

            if (underlyingType != null)
            {
                // check for not null
                Label notNullLabel = il.DefineLabel();

                var nullableHolder = il.DeclareLocal(propInfo.MemberType);
                il.Emit(OpCodes.Stloc, nullableHolder);
                il.Emit(OpCodes.Ldloca_S, nullableHolder);
                il.Emit(OpCodes.Call, sourceType.GetProperty("HasValue").GetGetMethod());
                il.Emit(OpCodes.Brtrue_S, notNullLabel);

                // it's null, just return null
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(notNullLabel);

                // it's not null, so unbox to the underlyingtype
                il.Emit(OpCodes.Ldloca, nullableHolder);
                il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod());

                // at this point we have de-nulled value, so use those converters
                sourceType = underlyingType;
            }

            // if the serializer isn't the default ToStringSerializer, and it can serialize the type properly, then use it
            // otherwise we'll use coersion or conversion
            var  targetDbType = DbParameterGenerator.LookupDbType(targetType);
            bool canSerialize = sourceType != typeof(string) &&
                                mapping.Serializer.GetType() != typeof(ToStringObjectSerializer) &&
                                mapping.Serializer.CanSerialize(sourceType, targetDbType);

            if (sourceType != targetType && canSerialize)
            {
                if (sourceType.IsValueType)
                {
                    il.Emit(OpCodes.Box, sourceType);
                }
                il.EmitLoadType(sourceType);
                StaticFieldStorage.EmitLoad(il, mapping.Serializer);
                il.Emit(OpCodes.Call, typeof(ObjectReader).GetMethod("SerializeObject", BindingFlags.NonPublic | BindingFlags.Static));
            }
            else
            {
                // attempt to convert the value
                // either way, we are putting it in an object variable, so box it
                if (TypeConverterGenerator.EmitConversionOrCoersion(il, sourceType, targetType))
                {
                    il.Emit(OpCodes.Box, targetType);
                }
                else
                {
                    il.Emit(OpCodes.Box, sourceType);
                }
            }

            il.MarkLabel(readyToSetLabel);
            il.Emit(OpCodes.Ret);

            return((Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>)));
        }
예제 #8
0
        private ObjectReader(IDbCommand command, Type type, IDataReader reader)
        {
            var provider = InsightDbProvider.For(command);

            // copy the schema and fix it
            SchemaTable = reader.GetSchemaTable().Copy();
            FixupSchemaNumericScale();
            FixupSchemaRemoveReadOnlyColumns();

            IsAtomicType = TypeHelper.IsAtomicType(type);
            if (!IsAtomicType)
            {
                // create a mapping, and only keep mappings that match our modified schema
                var mappings = ColumnMapping.Tables.CreateMapping(type, reader, null, null, null, 0, reader.FieldCount, true)
                               .Where(m => m != null).ToArray();

                int columnCount = SchemaTable.Rows.Count;
                _accessors   = new Func <object, object> [columnCount];
                _memberTypes = new Type[columnCount];

                for (int i = 0; i < columnCount; i++)
                {
                    var columnName = SchemaTable.Rows[i]["ColumnName"].ToString();
                    var mapping    = mappings.FirstOrDefault(m => String.Compare(m.ColumnName, columnName, StringComparison.OrdinalIgnoreCase) == 0);
                    if (mapping == null)
                    {
                        continue;
                    }
                    ClassPropInfo propInfo = mapping.ClassPropInfo;

                    // create a new anonymous method that takes an object and returns the value
                    var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true);
                    var il = dm.GetILGenerator();

                    // convert the object reference to the desired type
                    il.Emit(OpCodes.Ldarg_0);
                    if (type.IsValueType)
                    {
                        // access the field/property of a value type
                        var valueHolder = il.DeclareLocal(type);
                        il.Emit(OpCodes.Unbox_Any, type);
                        il.Emit(OpCodes.Stloc, valueHolder);
                        il.Emit(OpCodes.Ldloca_S, valueHolder);
                    }
                    else
                    {
                        il.Emit(OpCodes.Isinst, type);                                                          // cast object -> type
                    }
                    // get the value from the object
                    propInfo.EmitGetValue(il);

                    // if the type is nullable, handle nulls
                    Type sourceType     = propInfo.MemberType;
                    Type targetType     = (Type)SchemaTable.Rows[i]["DataType"];
                    Type underlyingType = Nullable.GetUnderlyingType(sourceType);
                    if (underlyingType != null)
                    {
                        // check for not null
                        Label notNullLabel = il.DefineLabel();

                        var nullableHolder = il.DeclareLocal(propInfo.MemberType);
                        il.Emit(OpCodes.Stloc, nullableHolder);
                        il.Emit(OpCodes.Ldloca_S, nullableHolder);
                        il.Emit(OpCodes.Call, sourceType.GetProperty("HasValue").GetGetMethod());
                        il.Emit(OpCodes.Brtrue_S, notNullLabel);

                        // it's null, just return null
                        il.Emit(OpCodes.Ldnull);
                        il.Emit(OpCodes.Ret);

                        il.MarkLabel(notNullLabel);

                        // it's not null, so unbox to the underlyingtype
                        il.Emit(OpCodes.Ldloca, nullableHolder);
                        il.Emit(OpCodes.Call, sourceType.GetProperty("Value").GetGetMethod());

                        // at this point we have de-nulled value, so use those converters
                        sourceType = underlyingType;
                    }

                    if (sourceType != targetType && !sourceType.IsValueType && sourceType != typeof(string))
                    {
                        // if the provider type is Xml, then serialize the value
                        var serializerMethod = mapping.Serializer.GetMethod("Serialize", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(object), typeof(Type) }, null);
                        if (serializerMethod == null)
                        {
                            throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Serializer type {0} needs the method 'public static string Serialize(object, Type)'", mapping.Serializer.Name));
                        }

                        il.EmitLoadType(sourceType);
                        il.Emit(OpCodes.Call, serializerMethod);
                    }
                    else
                    {
                        // attempt to convert the value
                        // either way, we are putting it in an object variable, so box it
                        if (TypeConverterGenerator.EmitConversionOrCoersion(il, sourceType, targetType))
                        {
                            il.Emit(OpCodes.Box, targetType);
                        }
                        else
                        {
                            il.Emit(OpCodes.Box, sourceType);
                        }
                    }

                    il.Emit(OpCodes.Ret);

                    _memberTypes[i] = propInfo.MemberType;
                    _accessors[i]   = (Func <object, object>)dm.CreateDelegate(typeof(Func <object, object>));
                }
            }
            else
            {
                // we are working off a single-column atomic type
                _memberTypes = new Type[1] {
                    type
                };
                _accessors = new Func <object, object>[] { o => o };
            }
        }
예제 #9
0
        /// <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));
        }
예제 #10
0
        private static DynamicMethod CreateClassDeserializerDynamicMethod(Type type, IDataReader reader, IRecordStructure structure, int startColumn, int columnCount, bool createNewObject, bool isRootObject, bool allowBindChild)
        {
            // if there are no columns detected for the class, then the deserializer is null
            if (columnCount == 0 && !isRootObject)
            {
                return(null);
            }

            var mappings = MapColumns(type, reader, startColumn, columnCount, structure, allowBindChild && isRootObject);

            // need to know the constructor for the object (except for structs)
            bool            isStruct    = type.GetTypeInfo().IsValueType;
            ConstructorInfo constructor = createNewObject ? SelectConstructor(type) : null;

            // the method can either be:
            // createNewObject => Func<IDataReader, T>
            // !createNewObject => Func<IDataReader, T, T>
            // create a new anonymous method that takes an IDataReader and returns the given type
            var dm = new DynamicMethod(
                String.Format(CultureInfo.InvariantCulture, "Deserialize-{0}-{1}", type.FullName, Guid.NewGuid()),
                type,
                createNewObject ? new[] { typeof(IDataReader) } : new[] { typeof(IDataReader), type },
                true);

            // get the il generator and put some local variables on the stack
            var il                  = dm.GetILGenerator();
            var localIndex          = il.DeclareLocal(typeof(int));
            var localResult         = il.DeclareLocal(type);
            var localValue          = il.DeclareLocal(typeof(object));
            var localIsNotAllDbNull = il.DeclareLocal(typeof(bool));

            // initialization
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stloc, localIndex);

            /////////////////////////////////////////////////////////////////////
            // read all of the values into local variables
            /////////////////////////////////////////////////////////////////////
            il.BeginExceptionBlock();
            var localValues = new LocalBuilder[mappings.Count];

            for (int index = 0; index < columnCount; index++)
            {
                var mapping = mappings[index];
                if (mapping == null)
                {
                    continue;
                }

                var member = mapping.Member;

                localValues[index] = il.DeclareLocal(member.MemberType);

                // need to call IDataReader.GetItem to get the value of the field
                il.Emit(OpCodes.Ldarg_0);
                IlHelper.EmitLdInt32(il, index + startColumn);

                // before we call it, put the current index into the index local variable
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Stloc, localIndex);

                // now call it
                il.Emit(OpCodes.Callvirt, _iDataReaderGetItem);

                // if handling a subobject, we check to see if the value is null
                if (startColumn > 0)
                {
                    var afterNullCheck = il.DefineLabel();
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Ldsfld, typeof(DBNull).GetField("Value"));
                    il.Emit(OpCodes.Ceq);
                    il.Emit(OpCodes.Brtrue, afterNullCheck);
                    il.Emit(OpCodes.Ldc_I4_1);
                    il.Emit(OpCodes.Stloc, localIsNotAllDbNull);
                    il.MarkLabel(afterNullCheck);
                }

                // store the value as a local variable in case type conversion fails
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Stloc, localValue);

                // convert the value and store it locally
                Type sourceType = reader.GetFieldType(index + startColumn);
                TypeConverterGenerator.EmitConvertValue(il, member.Name, sourceType, member.MemberType, mapping.Serializer);
                il.Emit(OpCodes.Stloc, localValues[index]);
            }

            /////////////////////////////////////////////////////////////////////
            // catch translation exceptions and rethrow
            /////////////////////////////////////////////////////////////////////
            il.BeginCatchBlock(typeof(Exception));                                              // stack => [Exception]
            il.Emit(OpCodes.Ldloc, localIndex);                                                 // push loc.0, stack => [Exception][index]
            il.Emit(OpCodes.Ldarg_0);                                                           // push arg.0, stack => [Exception][index][reader]
            il.Emit(OpCodes.Ldloc, localValue);                                                 // push loc.3, stack => [Exception][index][reader][value]
            il.Emit(OpCodes.Call, TypeConverterGenerator.CreateDataExceptionMethod);
            il.Emit(OpCodes.Throw);                                                             // stack => DataException
            il.EndExceptionBlock();

            /////////////////////////////////////////////////////////////////////
            // if this was a subobject and all of the values are null, then return the default for the object
            /////////////////////////////////////////////////////////////////////
            if (startColumn > 0)
            {
                var afterNullExit = il.DefineLabel();
                il.Emit(OpCodes.Ldloc, localIsNotAllDbNull);
                il.Emit(OpCodes.Brtrue, afterNullExit);
                TypeHelper.EmitDefaultValue(il, type);
                il.Emit(OpCodes.Ret);
                il.MarkLabel(afterNullExit);
            }

            /////////////////////////////////////////////////////////////////////
            // call the constructor
            /////////////////////////////////////////////////////////////////////
            if (createNewObject)
            {
                if (isStruct)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);
                    il.Emit(OpCodes.Initobj, type);
                    if (constructor != null)
                    {
                        il.Emit(OpCodes.Ldloca_S, localResult);
                    }
                }

                // if there is a constructor, then populate the values
                if (constructor != null)
                {
                    foreach (var p in constructor.GetParameters())
                    {
                        var mapping = mappings.Where(m => m != null).SingleOrDefault(m => m.Member.Name.IsIEqualTo(p.Name));
                        if (mapping != null)
                        {
                            il.Emit(OpCodes.Ldloc, localValues[mappings.IndexOf(mapping)]);
                        }
                        else
                        {
                            TypeHelper.EmitDefaultValue(il, p.ParameterType);
                        }
                    }
                }

                if (isStruct)
                {
                    if (constructor != null)
                    {
                        il.Emit(OpCodes.Call, constructor);
                    }
                }
                else
                {
                    il.Emit(OpCodes.Newobj, constructor);
                    il.Emit(OpCodes.Stloc, localResult);
                }
            }
            else
            {
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Stloc, localResult);
            }

            /////////////////////////////////////////////////////////////////////
            // for anything not passed to the constructor, copy the local values to the properties
            /////////////////////////////////////////////////////////////////////
            for (int index = 0; index < columnCount; index++)
            {
                var mapping = mappings[index];
                if (mapping == null)
                {
                    continue;
                }

                var member = mapping.Member;
                if (!member.CanSetMember)
                {
                    continue;
                }

                // don't set values that have already been set
                if (constructor != null && constructor.GetParameters().Any(p => p.Name.IsIEqualTo(mapping.Member.Name)))
                {
                    continue;
                }

                // load the address of the object we are working on
                if (isStruct)
                {
                    il.Emit(OpCodes.Ldloca_S, localResult);
                }
                else
                {
                    il.Emit(OpCodes.Ldloc, localResult);
                }

                // for deep mappings, go to the parent of the field that we are trying to set
                var nextLabel = il.DefineLabel();
                if (mapping.IsDeep)
                {
                    ClassPropInfo.EmitGetValue(type, mapping.Prefix, il);

                    // if the mapping parent is nullable, check to see if it is null.
                    // if so, pop the parent off the stack and move to the next field
                    if (!ClassPropInfo.FindMember(type, mapping.Prefix).MemberType.GetTypeInfo().IsValueType)
                    {
                        var notNullLabel = il.DefineLabel();
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Brtrue, notNullLabel);
                        il.Emit(OpCodes.Pop);
                        il.Emit(OpCodes.Br, nextLabel);
                        il.MarkLabel(notNullLabel);
                    }
                }

                // load the value from the local and set it on the object
                il.Emit(OpCodes.Ldloc, localValues[index]);
                member.EmitSetValue(il);
                il.MarkLabel(nextLabel);

                /////////////////////////////////////////////////////////////////////
                // stack should be [target] and ready for the next column
                /////////////////////////////////////////////////////////////////////
            }

            /////////////////////////////////////////////////////////////////////
            // load the return value from the local variable
            /////////////////////////////////////////////////////////////////////
            il.Emit(OpCodes.Ldloc, localResult);                                                        // ld loc.1 (target), stack => [target]
            il.Emit(OpCodes.Ret);

            // create the function
            return(dm);
        }
예제 #11
0
        /// <summary>
        /// Creates a converter from output parameters to an object of a given type.
        /// </summary>
        /// <param name="command">The command to analyze for the results.</param>
        /// <param name="type">The type to put the values into.</param>
        /// <returns>The converter method.</returns>
        private static Action <IDbCommand, object> CreateClassOutputParameterConverter(IDbCommand command, Type type)
        {
            // get the parameters
            List <IDataParameter> parameters = command.Parameters.Cast <IDataParameter>().ToList();

            // if there are no output parameters, then return an empty method
            if (!parameters.Cast <IDataParameter>().Any(p => p.Direction.HasFlag(ParameterDirection.Output)))
            {
                return (IDbCommand c, object o) => { }
            }
            ;

            // create a dynamic method
            Type typeOwner = type.HasElementType ? type.GetElementType() : type;

            // start creating a dynamic method
            var dm = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateOutputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true);
            var il = dm.GetILGenerator();

            var localParameters = il.DeclareLocal(typeof(IDataParameterCollection));

            // get the parameters collection from the command into loc.0
            il.Emit(OpCodes.Ldarg_0);                                                                           // push arg.0 (command), stack => [command]
            il.Emit(OpCodes.Callvirt, _iDbCommandGetParameters);                                                // call getparams, stack => [parameters]
            il.Emit(OpCodes.Stloc, localParameters);

            // go through all of the mappings
            var mappings = ColumnMapping.MapParameters(type, command, parameters);

            for (int i = 0; i < mappings.Count; i++)
            {
                var finishLabel = il.DefineLabel();

                // if there is no parameter for this property, then skip it
                var mapping = mappings[i];
                if (mapping == null)
                {
                    continue;
                }

                // if the property is readonly, then skip it
                var prop = mapping.Member;
                if (!prop.CanSetMember)
                {
                    continue;
                }

                // if the parameter is not output, then skip it
                IDataParameter parameter = parameters[i];
                if (parameter == null || !parameter.Direction.HasFlag(ParameterDirection.Output))
                {
                    continue;
                }

                // push the object on the stack. we will need it to set the value below
                il.Emit(OpCodes.Ldarg_1);

                // if this is a deep mapping, then get the parent object, and do a null test if its not a value type
                if (mapping.IsDeep)
                {
                    ClassPropInfo.EmitGetValue(type, mapping.Prefix, il);

                    if (!ClassPropInfo.FindMember(type, mapping.Prefix).MemberType.GetTypeInfo().IsValueType)
                    {
                        il.Emit(OpCodes.Dup);
                        var label = il.DefineLabel();
                        il.Emit(OpCodes.Brtrue, label);
                        il.Emit(OpCodes.Pop);               // pop the object before finishing
                        il.Emit(OpCodes.Br, finishLabel);
                        il.MarkLabel(label);
                    }
                }

                // get the parameter out of the collection
                il.Emit(OpCodes.Ldloc, localParameters);
                il.Emit(OpCodes.Ldstr, parameter.ParameterName);                 // push (parametername)
                il.Emit(OpCodes.Callvirt, _iDataParameterCollectionGetItem);

                // get the value out of the parameter
                il.Emit(OpCodes.Callvirt, _iDataParameterGetValue);

                // emit the code to convert the value and set it on the object
                TypeConverterGenerator.EmitConvertAndSetValue(il, _dbTypeToTypeMap[parameter.DbType], mapping);
                il.MarkLabel(finishLabel);
            }

            il.Emit(OpCodes.Ret);

            return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>)));
        }
예제 #12
0
        private static Action <IDbCommand, object> CreateClassInputParameterGenerator(IDbCommand command, Type type)
        {
            var provider   = InsightDbProvider.For(command);
            var parameters = provider.DeriveParameters(command);

            // special case if the parameters object is an IEnumerable or Array
            // look for the parameter that is a Structured object and pass the array to the TVP
            // note that string supports ienumerable, so exclude atomic types
            var enumerable = type.GetInterfaces().FirstOrDefault(i => i.GetTypeInfo().IsGenericType&& i.GetGenericTypeDefinition() == typeof(IEnumerable <>));

            if (enumerable != null && type != typeof(string) && parameters.OfType <IDataParameter>().Where(p => p.Direction.HasFlag(ParameterDirection.Input)).Count() == 1)
            {
                return((IDbCommand cmd, object o) =>
                {
                    // don't use the provider above. The command may be unwrapped by the time we get back here
                    var tableParameter = InsightDbProvider.For(cmd).CloneParameter(cmd, parameters.OfType <IDataParameter>().Single(p => p.Direction.HasFlag(ParameterDirection.Input)));
                    cmd.Parameters.Add(tableParameter);
                    ListParameterHelper.ConvertListParameter(tableParameter, o, cmd);
                });
            }

            // get the mapping of the properties for the type
            var mappings = ColumnMapping.MapParameters(type, command, parameters);

            // start creating a dynamic method
            Type typeOwner = type.HasElementType ? type.GetElementType() : type;
            var  dm        = new DynamicMethod(String.Format(CultureInfo.InvariantCulture, "CreateInputParameters-{0}", Guid.NewGuid()), null, new[] { typeof(IDbCommand), typeof(object) }, typeOwner, true);
            var  il        = dm.GetILGenerator();

            // copy the parameters into the command object
            var parametersLocal = il.DeclareLocal(typeof(IDataParameter[]));

            StaticFieldStorage.EmitLoad(il, provider);
            il.Emit(OpCodes.Ldarg_0);
            StaticFieldStorage.EmitLoad(il, parameters);
            il.Emit(OpCodes.Call, typeof(InsightDbProvider).GetMethod("CopyParameters", BindingFlags.NonPublic | BindingFlags.Instance));
            il.Emit(OpCodes.Stloc, parametersLocal);

            // go through all of the mappings
            for (int i = 0; i < mappings.Count; i++)
            {
                var mapping     = mappings[i];
                var dbParameter = parameters[i];

                // if there is no mapping for the parameter
                if (mapping == null)
                {
                    // sql will silently eat table parameters that are not specified, and that can be difficult to debug
                    if (provider.IsTableValuedParameter(command, dbParameter))
                    {
                        throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Table parameter {0} must be specified", dbParameter.ParameterName));
                    }

                    // unspecified input parameters get skipped
                    if (dbParameter.Direction == ParameterDirection.Input)
                    {
                        parameters[i] = null;
                    }

                    continue;
                }

                var memberType = mapping.Member.MemberType;
                var serializer = mapping.Serializer;

                // get the parameter
                il.Emit(OpCodes.Ldloc, parametersLocal);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldelem, typeof(IDataParameter));

                // look up the best type to use for the parameter
                DbType sqlType = LookupDbType(memberType, serializer, dbParameter.DbType);

                // give the provider an opportunity to fix up the template parameter (e.g. set UDT type names)
                provider.FixupParameter(command, dbParameter, sqlType, memberType, mapping.Member.SerializationMode);

                // give a chance to override the best guess parameter
                DbType overriddenSqlType = sqlType;
                if (sqlType != DbTypeEnumerable)
                {
                    overriddenSqlType = ColumnMapping.MapParameterDataType(memberType, command, dbParameter, sqlType);
                }

                ///////////////////////////////////////////////////////////////
                // We have a parameter, start handling all of the other types
                ///////////////////////////////////////////////////////////////
                if (overriddenSqlType != sqlType)
                {
                    sqlType            = overriddenSqlType;
                    dbParameter.DbType = sqlType;
                }

                ///////////////////////////////////////////////////////////////
                // Get the value from the object onto the stack
                ///////////////////////////////////////////////////////////////
                il.Emit(OpCodes.Ldarg_1);
                if (type.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Unbox_Any, type);
                }

                ///////////////////////////////////////////////////////////////
                // Special case support for enumerables. If the type is -1 (our workaround, then call the list parameter method)
                ///////////////////////////////////////////////////////////////
                if (sqlType == DbTypeEnumerable)
                {
                    // we have the parameter and the value as object, add the command
                    ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, typeof(ListParameterHelper).GetMethod("ConvertListParameter", BindingFlags.Static | BindingFlags.NonPublic));
                    continue;
                }

                Label readyToSetLabel = il.DefineLabel();
                ClassPropInfo.EmitGetValue(type, mapping.PathToMember, il, readyToSetLabel);

                // special conversions for timespan to datetime
                if ((sqlType == DbType.Time && dbParameter.DbType != DbType.Time) ||
                    (dbParameter.DbType == DbType.DateTime || dbParameter.DbType == DbType.DateTime2 || dbParameter.DbType == DbType.DateTimeOffset))
                {
                    IlHelper.EmitLdInt32(il, (int)dbParameter.DbType);
                    il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("ObjectToSqlDateTime"));
                }

                // if it's class type, boxed value type (in an object), or nullable, then we have to check for null
                var nullableUnderlyingType = Nullable.GetUnderlyingType(memberType);
                if (!memberType.GetTypeInfo().IsValueType || nullableUnderlyingType != null)
                {
                    Label notNull = il.DefineLabel();

                    // check to see if it's not null
                    il.Emit(OpCodes.Dup);
                    il.Emit(OpCodes.Brtrue, notNull);

                    // it's null. replace the value with DbNull
                    il.Emit(OpCodes.Pop);
                    il.Emit(OpCodes.Ldsfld, _dbNullValue);

                    // value is set to null. ready to set the property.
                    il.Emit(OpCodes.Br, readyToSetLabel);

                    // we know the value is not null
                    il.MarkLabel(notNull);
                }

                // some providers (notably npgsql > 4.0) don't convert enums to ints, so we do it for them
                if ((memberType != null && memberType.GetTypeInfo() != null && memberType.GetTypeInfo().IsEnum) ||
                    (nullableUnderlyingType != null && nullableUnderlyingType.GetTypeInfo() != null && nullableUnderlyingType.GetTypeInfo().IsEnum))
                {
                    var enumType = nullableUnderlyingType ?? memberType;

                    // ClassPropInfo.EmitGetValue has the enum boxed, so unbox, cast, and re-box
                    switch (dbParameter.DbType)
                    {
                    case DbType.Int16:
                        il.Emit(OpCodes.Unbox_Any, enumType);
                        il.Emit(OpCodes.Conv_I2);
                        il.Emit(OpCodes.Box, typeof(Int16));
                        break;

                    case DbType.Int32:
                        il.Emit(OpCodes.Unbox_Any, enumType);
                        il.Emit(OpCodes.Conv_I4);
                        il.Emit(OpCodes.Box, typeof(Int32));
                        break;

                    case DbType.Int64:
                        il.Emit(OpCodes.Unbox_Any, enumType);
                        il.Emit(OpCodes.Conv_I8);
                        il.Emit(OpCodes.Box, typeof(Int64));
                        break;
                    }
                }

                ///////////////////////////////////////////////////////////////
                // if this is a linq binary, convert it to a byte array
                ///////////////////////////////////////////////////////////////
                if (memberType == TypeHelper.LinqBinaryType)
                {
                    il.Emit(OpCodes.Callvirt, TypeHelper.LinqBinaryToArray);
                }
                else if (memberType == typeof(XmlDocument))
                {
                    // we are sending up an XmlDocument. ToString just returns the classname, so use the outerxml.
                    il.Emit(OpCodes.Callvirt, memberType.GetProperty("OuterXml").GetGetMethod());
                }
                else if (memberType == typeof(XDocument))
                {
                    // we are sending up an XDocument. Use ToString.
                    il.Emit(OpCodes.Callvirt, memberType.GetMethod("ToString", new Type[] { }));
                }
                else if (serializer != null && serializer.CanSerialize(memberType, sqlType))
                {
                    il.EmitLoadType(memberType);
                    StaticFieldStorage.EmitLoad(il, serializer);
                    il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SerializeParameterValue", BindingFlags.NonPublic | BindingFlags.Static));
                }

                ///////////////////////////////////////////////////////////////
                // p.Value = value
                ///////////////////////////////////////////////////////////////
                // push parameter is at top of method
                // value is above
                il.MarkLabel(readyToSetLabel);
                if (memberType == typeof(string))
                {
                    il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterStringValue", BindingFlags.NonPublic | BindingFlags.Static));
                }
                else if ((memberType == typeof(Guid?) || (memberType == typeof(Guid))) && dbParameter.DbType != DbType.Guid && command.CommandType == CommandType.StoredProcedure)
                {
                    il.Emit(OpCodes.Call, typeof(DbParameterGenerator).GetMethod("SetParameterGuidValue", BindingFlags.NonPublic | BindingFlags.Static));
                }
                else
                {
                    il.Emit(OpCodes.Callvirt, _iDataParameterSetValue);
                }
            }

            il.Emit(OpCodes.Ret);

            return((Action <IDbCommand, object>)dm.CreateDelegate(typeof(Action <IDbCommand, object>)));
        }
		/// <summary>
		/// Emit the IL to convert the current value on the stack and set the value of the object.
		/// </summary>
		/// <param name="il">The IL generator to output to.</param>
		/// <param name="sourceType">The current type of the value.</param>
		/// <param name="method">The set property method to call.</param>
		/// <remarks>
		///	Expects the stack to contain:
		///		Target Object
		///		Value to set
		/// The value is first converted to the type required by the method parameter, then sets the property.
		/// </remarks>
		/// <returns>A label that needs to be marked at the end of a succesful set.</returns>
		public static Label EmitConvertAndSetValue(ILGenerator il, Type sourceType, ClassPropInfo method)
		{
			// targetType - the target type we need to convert to
			// underlyingTargetType - if the target type is nullable, we need to look at the underlying target type
			// rawTargetType - if the underlying target type is enum, we need to look at the underlying target type for that
			// sourceType - this is the type of the data in the data set
			Type targetType = method.MemberType;
			Type underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;

			// some labels that we need
			Label isDbNullLabel = il.DefineLabel();
			Label finishLabel = il.DefineLabel();

			// if the value is DbNull, then we continue to the next item
			il.Emit(OpCodes.Dup);								// dup value, stack => [target][value][value]
			il.Emit(OpCodes.Isinst, typeof(DBNull));			// isinst DBNull:value, stack => [target][value-as-object][DBNull or null]
			il.Emit(OpCodes.Brtrue_S, isDbNullLabel);			// br.true isDBNull, stack => [target][value-as-object]

			// handle the special target types first
			if (targetType == typeof(char))
			{
				// char
				il.EmitCall(OpCodes.Call, _readChar, null);
			}
			else if (targetType == typeof(char?))
			{
				// char?
				il.EmitCall(OpCodes.Call, _readNullableChar, null);
			}
			else if (targetType == typeof(System.Data.Linq.Binary))
			{
				// unbox sql byte arrays to Linq.Binary

				// before: stack => [target][object-value]
				// after: stack => [target][byte-array-value]
				il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][byte-array]
				// before: stack => [target][byte-array-value]
				// after: stack => [target][Linq.Binary-value]
				il.Emit(OpCodes.Newobj, _linqBinaryCtor);
			}
			else if (targetType == typeof(XmlDocument))
			{
				// special handler for XmlDocuments

				// before: stack => [target][object-value]
				il.Emit(OpCodes.Call, _readXmlDocument);

				// after: stack => [target][xmlDocument]
			}
			else if (targetType == typeof(XDocument))
			{
				// special handler for XDocuments

				// before: stack => [target][object-value]
				il.Emit(OpCodes.Call, _readXDocument);

				// after: stack => [target][xDocument]
			}
			else if (sourceType == typeof(string) && targetType != typeof(string) && !targetType.IsValueType)
			{
				// we are getting a string from the database, but the target is not a string, but it's a reference type
				// assume the column is an xml data type and that we want to deserialize it

				// before: stack => [target][object-value]
				il.EmitLoadType(targetType);

				// after: stack => [target][object-value][memberType]
				il.Emit(OpCodes.Call, _deserializeXml);
				il.Emit(OpCodes.Unbox_Any, targetType);
			}
			else if (underlyingTargetType.IsEnum && sourceType == typeof(string))
			{
				var localString = il.DeclareLocal(typeof(string));

				// if we are converting a string to an enum, then parse it.
				// see if the value from the database is a string. if so, we need to parse it. If not, we will just try to unbox it.
				il.Emit(OpCodes.Isinst, typeof(string));			// is string, stack => [target][string]
				il.Emit(OpCodes.Stloc, localString);				// pop loc.2 (enum), stack => [target]

				// call enum.parse (type, value, true)
				il.EmitLoadType(underlyingTargetType);
				il.Emit(OpCodes.Ldloc, localString);				// push enum, stack => [target][enum-type][string]
				il.Emit(OpCodes.Ldc_I4_1);							// push true, stack => [target][enum-type][string][true]
				il.EmitCall(OpCodes.Call, _enumParse, null);		// call Enum.Parse, stack => [target][enum-as-object]

				// Enum.Parse returns an object, which we need to unbox to the enum value
				il.Emit(OpCodes.Unbox_Any, underlyingTargetType);
			}
			else
			{
				// this isn't a system value type, so unbox to the type the reader is giving us (this is a system type, hopefully)
				// now we have an unboxed sourceType
				il.Emit(OpCodes.Unbox_Any, sourceType);

				// look for a constructor that takes the type as a parameter
				Type[] sourceTypes = new Type[] { sourceType };
				ConstructorInfo ci = targetType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, sourceTypes, null);
				if (ci != null)
				{
					// if the constructor only takes nullable types, warn the programmer
					if (Nullable.GetUnderlyingType(ci.GetParameters()[0].ParameterType) != null)
						throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Class {0} must provide a constructor taking a parameter of type {1}. Nullable parameters are not supported.", targetType, sourceType));

					il.Emit(OpCodes.Newobj, ci);
				}
				else
				{
					// attempt to convert the value to the target type
					if (!EmitConversionOrCoersion(il, sourceType, targetType))
					{
						if (sourceType != targetType)
						{
							throw new InvalidOperationException(String.Format(
								CultureInfo.InvariantCulture,
								"Field {0} cannot be converted from {1} to {2}. Create a conversion constructor or conversion operator.",
								method.Name,
								sourceType,
								targetType));
						}
					}

					// if the target is nullable, then construct the nullable from the data
					if (Nullable.GetUnderlyingType(targetType) != null)
						il.Emit(OpCodes.Newobj, targetType.GetConstructor(new[] { underlyingTargetType }));
				}
			}

			/////////////////////////////////////////////////////////////////////
			// now the stack has [target][value-unboxed]. we can set the value now
			method.EmitSetValue(il);

			// stack is now EMPTY
			/////////////////////////////////////////////////////////////////////

			/////////////////////////////////////////////////////////////////////
			// jump over our DBNull handler
			il.Emit(OpCodes.Br_S, finishLabel);
			/////////////////////////////////////////////////////////////////////

			/////////////////////////////////////////////////////////////////////
			// cleanup after IsDBNull.
			/////////////////////////////////////////////////////////////////////
			il.MarkLabel(isDbNullLabel);							// stack => [target][value]
			il.Emit(OpCodes.Pop);									// pop value, stack => [target]

			// if the type is an object, set the value to null
			// this is necessary for overwriting output parameters,
			// as well as overwriting any properties that may be set in the constructor of the object
			if (!method.MemberType.IsValueType)
			{
				il.Emit(OpCodes.Ldnull);							// push null
				method.EmitSetValue(il);
			}
			else
			{
				// we didn't call setvalue, so pop the target object off the stack
				il.Emit(OpCodes.Pop);								// pop target, stack => [empty]
			}

			return finishLabel;
		}
		/// <summary>
		/// Create accessors to pull data from the object of the given type for the given schema.
		/// </summary>
		/// <param name="type">The type to analyze.</param>
		/// <param name="identity">The schema identity to analyze.</param>
		/// <returns>A list of accessor functions to get values from the type.</returns>
		private FieldReaderData CreateFieldReaderData(Type type, SchemaIdentity identity)
		{
			FieldReaderData readerData = new FieldReaderData();

			readerData.Accessors = new List<Func<object, object>>();
			readerData.MemberTypes = new List<Type>();

			for (int i = 0; i < identity.SchemaTable.Rows.Count; i++)
			{
				// get the name of the column
				string name = _schemaTable.Rows[i]["ColumnName"].ToString();

				// create a new anonymous method that takes an object and returns the value
				var dm = new DynamicMethod(string.Format(CultureInfo.InvariantCulture, "GetValue-{0}-{1}", type.FullName, Guid.NewGuid()), typeof(object), new[] { typeof(object) }, true);
				var il = dm.GetILGenerator();

				il.Emit(OpCodes.Ldarg_0);						// push object argument
				il.Emit(OpCodes.Isinst, type);					// cast object -> type

				// get the value from the object
				ClassPropInfo propInfo = new ClassPropInfo(type, name);
				propInfo.EmitGetValue(il);
				propInfo.EmitBox(il);

				il.Emit(OpCodes.Ret);

				readerData.MemberTypes.Add(propInfo.MemberType);
				readerData.Accessors.Add((Func<object, object>)dm.CreateDelegate(typeof(Func<object, object>)));
			}

			return readerData;
		}