简体   繁体   English

使用Reflection.Emit从SqlDataReader填充可空类型

[英]Populating nullable type from SqlDataReader using Reflection.Emit

When trying to set value for any nullable datatype using reflection.emit, the values for nullable data types are not populated. 尝试使用reflection.emit设置任何可为空的数据类型的值时,不会填充可为空的数据类型的值。

for eg 例如

public class Employee
{
    public int Id { get;set;}
    public string? Name {get;set;}
    public DateTime? Joined {get;set;}
    public bool? IsManager {get;set;}
}

Here are my reflection.emit code to populate sql data reader: 这是我的Reflection.emit代码,用于填充sql数据阅读器:

        private static readonly MethodInfo GetValueMethod = typeof(SqlDataReader).GetMethod("get_Item", new Type[] { typeof(int) });
        private static readonly MethodInfo IsDBNullMethod = typeof(SqlDataReader).GetMethod("IsDBNull", new Type[] { typeof(int) }); 

        DynamicMethod method = new DynamicMethod("DynamicCreateMapping", typeof(T), new Type[] { typeof(IDataRecord) }, typeof(T), true);
        ILGenerator generator = method.GetILGenerator();

        LocalBuilder result = generator.DeclareLocal(typeof(T));
        generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
        generator.Emit(OpCodes.Stloc, result);

        for (int i = 0; i < reader.FieldCount; i++)
        {
            PropertyInfo propertyInfo = typeof(T).GetProperty(reader.GetName(i));
            Label endIfLabel = generator.DefineLabel();

            if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
            {
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                generator.Emit(OpCodes.Callvirt, IsDBNullMethod);
                generator.Emit(OpCodes.Brtrue, endIfLabel);

                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                generator.Emit(OpCodes.Callvirt, GetValueMethod);
                generator.Emit(OpCodes.Unbox_Any, reader.GetFieldType(i));
                generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());

                generator.MarkLabel(endIfLabel);
            }
        }

        generator.Emit(OpCodes.Ldloc, result);
        generator.Emit(OpCodes.Ret);

I know the problem exist on the below line but not sure how to fix it: 我知道以下行中存在问题,但不确定如何解决:

generator.Emit(OpCodes.Unbox_Any, reader.GetFieldType(i));

I try to set but it failed using: 我尝试设置,但使用以下命令失败:

generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);

So how to match the datatype from class and not from the sql data reader? 那么如何从类而不是从sql数据读取器匹配数据类型呢?

I fixed it after breaking head for quite sometime. 我断了一段时间后才修好它。 Thanks to the link [ http://tillitsonpaper.blogspot.co.uk/2011/05/mapping-data-reader-to-object.html][1] . 感谢链接[ http://tillitsonpaper.blogspot.co.uk/2011/05/mapping-data-reader-to-object.html][1] I did modified few codes and am pasting it here so that it might help newbie who has very little knowledge of reflection.Emit like me. 我修改了很少的代码并将其粘贴到此处,以便对那些对反射知之甚少的新手有所帮助。

I added condition to check for Boolean and Enum types: 我添加了条件来检查布尔值和枚举类型:

    if (propertyInfo.PropertyType.IsEnum || propertyInfo.PropertyType == typeof(Boolean))
     {
           generator.Emit(OpCodes.Unbox_Any, reader.GetFieldType(i));
     }
     else
     {
         generator.Emit(OpCodes.Call, GetConverterMethod(propertyInfo.PropertyType));
     }

The else part above will handle any type including nullable types, The GetConverterMethod implementation is shown below: 上面的else部分将处理任何类型,包括可空类型,GetConverterMethod实现如下所示:

        private MethodInfo GetConverterMethod(Type type)
        {
            switch (type.Name.ToUpper())
            {
                case "INT16":
                    return CreateConverterMethodInfo("ToInt16");
                case "INT32":
                    return CreateConverterMethodInfo("ToInt32");
                case "INT64":
                    return CreateConverterMethodInfo("ToInt64");
                case "SINGLE":
                    return CreateConverterMethodInfo("ToSingle");
                case "BOOLEAN":
                    return CreateConverterMethodInfo("ToBoolean");
                case "STRING":
                    return CreateConverterMethodInfo("ToString");
                case "DATETIME":
                    return CreateConverterMethodInfo("ToDateTime");
                case "DECIMAL":
                    return CreateConverterMethodInfo("ToDecimal");
                case "DOUBLE":
                    return CreateConverterMethodInfo("ToDouble");
                case "GUID":
                    return CreateConverterMethodInfo("ToGuid");
                case "BYTE[]":
                    return CreateConverterMethodInfo("ToBytes");
                case "BYTE":
                    return CreateConverterMethodInfo("ToByte");
                case "NULLABLE`1":
                    {
                        if (type == typeof(DateTime?))
                        {
                            return CreateConverterMethodInfo("ToDateTimeNull");
                        }
                        else if (type == typeof(Int32?))
                        {
                            return CreateConverterMethodInfo("ToInt32Null");
                        }
                        else if (type == typeof(Boolean?))
                        {
                            return CreateConverterMethodInfo("ToBooleanNull");
                        }
                        break;
                    }
            }
            return null;
        }

        private MethodInfo CreateConverterMethodInfo(string method)
        {
            return typeof(Converter).GetMethod(method, new Type[] { typeof(object) });
        }

The Converter Class code is below: 转换器类代码如下:

    public class Converter
    {
        public static Int16 ToInt16(object value)
        {
            return ChangeType<Int16>(value);
        }

        public static Int32 ToInt32(object value)
        {
            return ChangeType<Int32>(value);
        }

        public static Int64 ToInt64(object value)
        {
            return ChangeType<Int64>(value);
        }

        public static Single ToSingle(object value)
        {
            return ChangeType<Single>(value);
        }

        public static Boolean ToBoolean(object value)
        {
            return ChangeType<Boolean>(value);
        }

        public static System.String ToString(object value)
        {
            return ChangeType<System.String>(value);
        }

        public static DateTime ToDateTime(object value)
        {
            return ChangeType<DateTime>(value);
        }

        public static Decimal ToDecimal(object value)
        {
            return ChangeType<Decimal>(value);
        }

        public static Double ToDouble(object value)
        {
            return ChangeType<Double>(value);
        }

        public static Guid ToGuid(object value)
        {
            return ChangeType<Guid>(value);
        }

        public static Byte ToByte(object value)
        {
            return ChangeType<Byte>(value);
        }

        public static Byte[] ToBytes(object value)
        {
            return ChangeType<Byte[]>(value);
        }
        public static DateTime? ToDateTimeNull(object value)
        {
            return ChangeType<DateTime?>(value);
        }

        public static System.Int32? ToInt32Null(object value)
        {
            return ChangeType<Int32?>(value);
        }

        public static Boolean? ToBooleanNull(object value)
        {
            return ChangeType<Boolean?>(value);
        }

        private static T ChangeType<T>(object value)
        {
            var t = typeof(T);

            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                if (value == null)
                {
                    return default(T);
                }

                t = Nullable.GetUnderlyingType(t); ;
            }

            return (T)Convert.ChangeType(value, t);
        }
    }

Might not be very elegant way of doing but this works for any nullable types as well. 可能不是很优雅的做法,但这也适用于任何可为null的类型。 Hope this helps to someone who is struggling to get Nullable types working with Reflection.Emit. 希望这对那些努力使Nullable类型与Reflection.Emit一起使用的人有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM