示例#1
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>)));
        }
        private static void GenerateReturnsStructure(MethodInfo interfaceMethod, ILGenerator mIL)
        {
            // if we are returning a task, unwrap the task result
            var returnType = interfaceMethod.ReturnType;

            if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task <>))
            {
                returnType = returnType.GetGenericArguments()[0];
            }

            // if there are returns attributes specified, then build the structure for the coder
            var returnsAttributes = interfaceMethod.GetCustomAttributes(true).OfType <RecordsetAttribute>().ToDictionary(r => r.Index);

            if (returnType.IsSubclassOf(typeof(Results)) && !returnType.Name.StartsWith("Results`", StringComparison.OrdinalIgnoreCase))
            {
                // we have a return that is derived from results
                mIL.Emit(OpCodes.Ldsfld, typeof(DerivedResultsReader <>).MakeGenericType(returnType).GetField("Default"));
            }
            else
            {
                bool isSingle = !returnType.IsSubclassOf(typeof(Results)) && !IsGenericListType(returnType);

                // we are returning results<T...>, or IList<T...>
                var  returnTypeArgs = isSingle ? new Type[] { returnType } : returnType.GetGenericArguments();
                Type currentType    = null;

                // go through all of the type arguments or recordsets
                int returnIndex = 0;
                for (int i = 0; i < Math.Max(returnTypeArgs.Length, returnsAttributes.Keys.MaxOrDefault(k => k + 1)); i++)
                {
                    RecordsetAttribute r;
                    returnsAttributes.TryGetValue(i, out r);
                    var types = (r != null) ? r.Types : new Type[] { returnTypeArgs[i] };

                    // grab the records field for the appropriate OneToOne mapping
                    mIL.Emit(OpCodes.Ldsfld, Query.GetOneToOneType(types).GetField("Records", BindingFlags.Public | BindingFlags.Static));

                    // keep track of the type that we are returning
                    if (r == null || !r.IsChild)
                    {
                        returnIndex++;
                    }

                    if (i == 0)
                    {
                        // start the chain of calls
                        if (isSingle)
                        {
                            var recordReader = typeof(IRecordReader <>).MakeGenericType(returnType);
                            var readerType   = typeof(SingleReader <>).MakeGenericType(returnType);
                            var constructor  = readerType.GetConstructor(new Type[] { recordReader });
                            mIL.Emit(OpCodes.Newobj, constructor);
                            currentType = readerType;
                        }
                        else if (returnType.IsSubclassOf(typeof(Results)))
                        {
                            var method = typeof(Query).GetMethod("ReturnsResults", BindingFlags.Public | BindingFlags.Static).MakeGenericMethod(types[0]);
                            currentType = method.ReturnType;
                            mIL.Emit(OpCodes.Call, method);
                        }
                        else
                        {
                            // IList<T> or List<T>, etc...
                            var oneToOneBaseType = typeof(OneToOne <>).MakeGenericType(types[0]);

                            currentType = typeof(ListReader <>).MakeGenericType(types[0]);
                            Type readerType = currentType;

                            // if we're not returning an IList, then we need to insert the reader adapter class
                            if (returnType.GetGenericTypeDefinition() != typeof(IList <>))
                            {
                                readerType = typeof(ListReaderAdapter <,>).MakeGenericType(returnType, types[0]);
                            }

                            mIL.Emit(OpCodes.Newobj, readerType.GetConstructor(new Type[] { oneToOneBaseType }));
                        }
                    }
                    else if (r != null && r.IsChild)
                    {
                        // the parent is single and we haven't overridden the id field
                        var parentType = currentType.GetGenericArguments()[0];
                        var childType  = types[0];

                        var        list       = ChildMapperHelper.GetListSetter(parentType, childType, r.Into);
                        var        listMethod = typeof(ClassPropInfo).GetMethod("CreateSetMethod").MakeGenericMethod(parentType, typeof(List <>).MakeGenericType(childType)).Invoke(list, Parameters.EmptyArray);
                        MethodInfo method;

                        if (r.Id == null && TypeIsSingleReader(currentType))
                        {
                            // previous and recordReader are on the stack, add the id and list method
                            StaticFieldStorage.EmitLoad(mIL, listMethod, _moduleBuilder);

                            method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static)
                                     .Single(
                                mi => mi.Name == "ThenChildren" &&
                                mi.GetGenericArguments().Length == 2 &&
                                currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name)
                                     .MakeGenericMethod(
                                new Type[]
                            {
                                parentType,                                                             // TParent
                                childType,                                                              // TChild
                            });
                        }
                        else
                        {
                            var recordReaderType = typeof(RecordReader <>).MakeGenericType(childType);

                            var parentid       = ChildMapperHelper.GetIDAccessor(parentType, r.Id);
                            var parentIdType   = parentid.MemberType;
                            var parentIdMethod = parentid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(parentType, parentIdType).Invoke(parentid, Parameters.EmptyArray);

                            // if groupby is specified, then convert the RecordReader to an IChildRecordReader by groupby
                            if (r.GroupBy != null)
                            {
                                var childid = ChildMapperHelper.FindParentIDAccessor(childType, r.GroupBy, parentType);
                                if (childid == null)
                                {
                                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot find GroupBy {0} on {1}", r.GroupBy, childType.FullName));
                                }

                                var getMethod = childid.GetType().GetMethod("CreateGetMethod").MakeGenericMethod(childType, parentIdType).Invoke(childid, Parameters.EmptyArray);
                                StaticFieldStorage.EmitLoad(mIL, getMethod, _moduleBuilder);

                                var groupByMethod = recordReaderType.GetMethod("GroupBy").MakeGenericMethod(parentIdType);
                                mIL.Emit(OpCodes.Call, groupByMethod);

                                recordReaderType = groupByMethod.ReturnType;
                            }

                            // previous and recordReader are on the stack, add the id and list methods
                            StaticFieldStorage.EmitLoad(mIL, parentIdMethod, _moduleBuilder);
                            StaticFieldStorage.EmitLoad(mIL, listMethod, _moduleBuilder);

                            method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static)
                                     .Single(
                                mi => mi.Name == "ThenChildren" &&
                                mi.GetGenericArguments().Length == 3 &&
                                currentType.GetGenericTypeDefinition().Name == mi.GetParameters()[0].ParameterType.Name &&
                                mi.GetParameters()[1].ParameterType.Name == recordReaderType.Name &&
                                mi.GetParameters().Any(p => String.Compare(p.Name, "id", StringComparison.OrdinalIgnoreCase) == 0))
                                     .MakeGenericMethod(
                                new Type[]
                            {
                                parentType,                                                             // TParent
                                childType,                                                              // TChild
                                parentIdType                                                            // TId
                            });
                        }

                        mIL.Emit(OpCodes.Call, method);
                        currentType = method.ReturnType;
                    }
                    else
                    {
                        var method = typeof(Query).GetMethods(BindingFlags.Public | BindingFlags.Static)
                                     .Single(mi => mi.Name == "Then" && mi.GetGenericArguments().Length == returnIndex && mi.GetParameters()[0].ParameterType.Name.StartsWith("IQueryReader", StringComparison.OrdinalIgnoreCase))
                                     .MakeGenericMethod(returnTypeArgs.Take(returnIndex).ToArray());
                        mIL.Emit(OpCodes.Call, method);
                        currentType = method.ReturnType;
                    }
                }
            }
        }
示例#3
0
        /// <summary>
        /// Emits the IL to convert a value to a target type.
        /// </summary>
        /// <param name="il">The IL generator to output to.</param>
        /// <param name="memberName">The name of the member being converted.</param>
        /// <param name="sourceType">The source type.</param>
        /// <param name="targetType">The target type.</param>
        /// <param name="serializer">The serializer to use to deserialize the value.</param>
        public static void EmitConvertValue(ILGenerator il, string memberName, Type sourceType, Type targetType, IDbObjectSerializer serializer)
        {
            // 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 underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType;

            // some labels that we need
            var   finishLabel   = il.DefineLabel();
            Label isDbNullLabel = 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 == TypeHelper.LinqBinaryType)
            {
                // 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, TypeHelper.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 (serializer != null && serializer.CanDeserialize(sourceType, targetType))
            {
                // we are getting a string from the database, but the target is not a string, and it's a reference type
                // assume the column is a serialized data type and that we want to deserialize it
                il.EmitLoadType(targetType);
                StaticFieldStorage.EmitLoad(il, serializer);
                il.Emit(OpCodes.Call, typeof(TypeConverterGenerator).GetMethod("DeserializeObject", BindingFlags.NonPublic | BindingFlags.Static));
                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 if (EmitConstructorConversion(il, sourceType, targetType))
            {
                // target type can be constructed from source type
            }
            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);

                if (sourceType != targetType)
                {
                    // 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.",
                                                                    memberName,
                                                                    sourceType.AssemblyQualifiedName,
                                                                    targetType.AssemblyQualifiedName));
                        }
                    }

                    // 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 }));
                    }
                }
            }

            /////////////////////////////////////////////////////////////////////
            // convert DBNull to default of the given type
            il.Emit(OpCodes.Br_S, finishLabel);
            il.MarkLabel(isDbNullLabel);
            il.Emit(OpCodes.Pop);
            TypeHelper.EmitDefaultValue(il, targetType);

            il.MarkLabel(finishLabel);
        }
示例#4
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>)));
        }
        private ObjectReader(IDbCommand command, Type type, IDataReader reader)
        {
            var provider = InsightDbProvider.For(command);

            // create a mapping, and only keep mappings that match our modified schema
            var mappings = ColumnMapping.MapColumns(type, reader).ToList();

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

            IsAtomicType = TypeHelper.IsAtomicType(type);
            if (!IsAtomicType)
            {
                int columnCount = SchemaTable.Rows.Count;
                _accessors   = new Func <object, object> [columnCount];
                _memberTypes = new Type[columnCount];

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

                    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     = (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))
                    {
                        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);

                    _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 };
            }
        }