csharp/1448376744/Dapper.Linq/src/Dapper.Common/Providers/EntityMapperProvider.cs

EntityMapperProvider.cs
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace Dapper
{
    /// 
    /// 实体转换映射器
    /// 
    public interface IEnsatyMapperProvider
    {
        /// 
        /// 获取实体序列化转换器
        /// 
        /// 
        /// 
        /// 
        Func GetSerializer(IDataRecord record);
        /// 
        /// 获取动态实体列化转换器
        /// 
        /// 
        Func GetSerializer();
        /// 
        /// 获取参数解码器
        /// 
        /// 
        /// 
        Func GetDeserializer(Type type);
    }

    /// 
    /// 默认实体映射器
    /// 
    public clast EnsatyMapperProvider : IEnsatyMapperProvider
    {
        private readonly MemberMapper _memberMapper = new MemberMapper();
        private readonly ConcurrentDictionary _serializers
            = new ConcurrentDictionary();
        private readonly ConcurrentDictionary _deserializers
            = new ConcurrentDictionary();
        public EnsatyMapperProvider()
        {

        }
        private struct SerializerKey : IEquatable
        {
            private string[] Names { get; set; }
            private Type Type { get; set; }
            public override bool Equals(object obj)
            {
                return obj is SerializerKey && Equals((SerializerKey)obj);
            }
            public bool Equals(SerializerKey other)
            {
                if (Type != other.Type)
                {
                    return false;
                }
                else if (Names == other.Names)
                {
                    return true;
                }
                else if (Names.Length != other.Names.Length)
                {
                    return false;
                }
                else
                {
                    for (int i = 0; i < Names.Length; i++)
                    {
                        if (Names[i] != other.Names[i])
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }
            public override int GetHashCode()
            {
                return Type.GetHashCode();
            }
            public SerializerKey(Type type, string[] names)
            {
                Type = type;
                Names = names;
            }
        }
        /// 
        /// 获取实体映射器
        /// 
        public Func GetSerializer(IDataRecord record)
        {
            string[] names = new string[record.FieldCount];
            for (int i = 0; i < record.FieldCount; i++)
            {
                names[i] = record.GetName(i);
            }
            var key = new SerializerKey(typeof(T), names.Length == 1 ? null : names);
            var handler = _serializers.GetOrAdd(key, k =>
             {
                 return CreateTypeSerializerHandler(_memberMapper, record);
             });
            return handler as Func;
        }
        /// 
        /// 获取动态映射器
        /// 
        public Func GetSerializer()
        {
            return (reader) =>
            {
                dynamic obj = new System.Dynamic.ExpandoObject();
                var row = (IDictionary)obj;
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    var name = reader.GetName(i);
                    row.Add(name, reader.GetValue(i));
                }
                return row;
            };
        }
        /// 
        /// 获取实体解构器
        /// 
        public Func GetDeserializer(Type type)
        {
            if (type == typeof(Dictionary))
            {
                return (object param) => param as Dictionary;
            }
            var handler = _deserializers.GetOrAdd(type, t =>
            {
                return CreateTypeDeserializerHandler(type) as Func;
            });
            return handler;
        }
        /// 
        /// 创建动态方法
        /// 
        /// 
        /// 
        private Func CreateTypeDeserializerHandler(Type type)
        {
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var methodName = $"{type.Name}Deserializer{Guid.NewGuid():N}";
            var dynamicMethod = new DynamicMethod(methodName, typeof(Dictionary), new Type[] { typeof(object) }, type, true);
            var generator = dynamicMethod.GetILGenerator();
            LocalBuilder ensatyLocal1 = generator.DeclareLocal(typeof(Dictionary));
            LocalBuilder ensatyLocal2 = generator.DeclareLocal(type);
            generator.Emit(OpCodes.Newobj, typeof(Dictionary).GetConstructor(Type.EmptyTypes));
            generator.Emit(OpCodes.Stloc, ensatyLocal1);
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Castclast, type);
            generator.Emit(OpCodes.Stloc, ensatyLocal2);
            foreach (var item in properties)
            {
                generator.Emit(OpCodes.Ldloc, ensatyLocal1);
                generator.Emit(OpCodes.Ldstr, item.Name);
                generator.Emit(OpCodes.Ldloc, ensatyLocal2);
                generator.Emit(OpCodes.Callvirt, item.GetGetMethod());
                if (item.PropertyType.IsValueType)
                {
                    generator.Emit(OpCodes.Box, item.PropertyType);
                }
                var addMethod = typeof(Dictionary).GetMethod(nameof(Dictionary.Add), new Type[] { typeof(string), typeof(object) });
                generator.Emit(OpCodes.Callvirt, addMethod);
            }
            generator.Emit(OpCodes.Ldloc, ensatyLocal1);
            generator.Emit(OpCodes.Ret);
            return dynamicMethod.CreateDelegate(typeof(Func)) as Func;
        }
        /// 
        /// 创建动态方法
        /// 
        /// 
        /// 
        /// 
        /// 
        private Func CreateTypeSerializerHandler(MemberMapper mapper, IDataRecord record)
        {
            var type = typeof(T);
            var methodName = $"Serializer{Guid.NewGuid():N}";
            var dynamicMethod = new DynamicMethod(methodName, type, new Type[] { typeof(IDataRecord) }, type, true);
            var generator = dynamicMethod.GetILGenerator();
            LocalBuilder local = generator.DeclareLocal(type);
            var dataInfos = new DataReaderCellInfo[record.FieldCount];
            for (int i = 0; i < record.FieldCount; i++)
            {
                var dataname = record.GetName(i);
                var datatype = record.GetFieldType(i);
                var typename = record.GetDataTypeName(i);
                dataInfos[i] = new DataReaderCellInfo(i, typename, datatype, dataname);
            }
            if (dataInfos.Length == 1 && (type.IsValueType || type == typeof(string) || type == typeof(object)))
            {
                var dataInfo = dataInfos.First();
                var convertMethod = mapper.FindConvertMethod(type, dataInfo.DataType);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, 0);
                if (convertMethod.IsVirtual)
                    generator.Emit(OpCodes.Callvirt, convertMethod);
                else
                    generator.Emit(OpCodes.Call, convertMethod);
                if (type == typeof(object) && convertMethod.ReturnType.IsValueType)
                {
                    generator.Emit(OpCodes.Box, convertMethod.ReturnType);
                }
                generator.Emit(OpCodes.Stloc, local);
                generator.Emit(OpCodes.Ldloc, local);
                generator.Emit(OpCodes.Ret);
                return dynamicMethod.CreateDelegate(typeof(Func)) as Func;
            }
            var constructor = mapper.FindConstructor(type);
            if (constructor.GetParameters().Length > 0)
            {
                var parameters = constructor.GetParameters();
                var locals = new LocalBuilder[parameters.Length];
                for (int i = 0; i < locals.Length; i++)
                {
                    locals[i] = generator.DeclareLocal(parameters[i].ParameterType);
                }
                for (int i = 0; i < locals.Length; i++)
                {
                    var item = mapper.FindConstructorParameter(dataInfos, parameters[i]);
                    if (item == null)
                    {
                        continue;
                    }
                    var convertMethod = mapper.FindConvertMethod(parameters[i].ParameterType, item.DataType);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, item.Ordinal);
                    if (convertMethod.IsVirtual)
                        generator.Emit(OpCodes.Callvirt, convertMethod);
                    else
                        generator.Emit(OpCodes.Call, convertMethod);
                    generator.Emit(OpCodes.Stloc, locals[i]);
                }
                for (int i = 0; i < locals.Length; i++)
                {
                    generator.Emit(OpCodes.Ldloc, locals[i]);
                }
                generator.Emit(OpCodes.Newobj, constructor);
                generator.Emit(OpCodes.Stloc, local);
                generator.Emit(OpCodes.Ldloc, local);
                generator.Emit(OpCodes.Ret);
                return dynamicMethod.CreateDelegate(typeof(Func)) as Func;
            }
            else
            {
                var properties = type.GetProperties();
                generator.Emit(OpCodes.Newobj, constructor);
                generator.Emit(OpCodes.Stloc, local);
                foreach (var item in dataInfos)
                {
                    var property = mapper.FindMember(properties, item) as PropertyInfo;
                    if (property == null)
                    {
                        continue;
                    }
                    var convertMethod = mapper.FindConvertMethod(property.PropertyType, item.DataType);
                    if (convertMethod == null)
                    {
                        continue;
                    }
                    int i = record.GetOrdinal(item.DataName);
                    generator.Emit(OpCodes.Ldloc, local);
                    generator.Emit(OpCodes.Ldarg_0);
                    generator.Emit(OpCodes.Ldc_I4, i);
                    if (convertMethod.IsVirtual)
                        generator.Emit(OpCodes.Callvirt, convertMethod);
                    else
                        generator.Emit(OpCodes.Call, convertMethod);
                    generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
                }
                generator.Emit(OpCodes.Ldloc, local);
                generator.Emit(OpCodes.Ret);
                return dynamicMethod.CreateDelegate(typeof(Func)) as Func;
            }
        }
    }

    /// 
    /// DataReader中的行信息
    /// 
    clast DataReaderCellInfo
    {
        public string TypeName { get; set; }
        public Type DataType { get; set; }
        public string DataName { get; set; }
        public int Ordinal { get; set; }
        public DataReaderCellInfo(int ordinal, string typeName, Type dataType, string dataName)
        {
            Ordinal = ordinal;
            TypeName = typeName;
            DataType = dataType;
            DataName = dataName;
        }
    }

    /// 
    /// 返回数据记录到Csharp类型的策略
    /// 
    clast MemberMapper
    {
        /// 
        /// Find parametric constructors.
        /// If there is no default constructor, the constructor with the most parameters is returned.
        /// 
        public ConstructorInfo FindConstructor(Type csharpType)
        {
            var constructor = csharpType.GetConstructor(Type.EmptyTypes);
            if (constructor == null)
            {
                var constructors = csharpType.GetConstructors();
                constructor = constructors.Where(a => a.GetParameters().Length == constructors.Max(s => s.GetParameters().Length)).FirstOrDefault();
            }
            return constructor;
        }

        /// 
        /// Returns field information based on parameter information
        /// 
        public DataReaderCellInfo FindConstructorParameter(DataReaderCellInfo[] dataInfos, ParameterInfo parameterInfo)
        {
            foreach (var item in dataInfos)
            {
                if (item.DataName.Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return item;
                }
                else if (item.DataName.Replace("_", "").Equals(parameterInfo.Name, StringComparison.OrdinalIgnoreCase))
                {
                    return item;
                }
            }
            return null;
        }

        /// 
        /// Returns attribute information based on field information
        /// 
        public MemberInfo FindMember(MemberInfo[] properties, DataReaderCellInfo dataInfo)
        {
            foreach (var item in properties)
            {
                if (item.Name.Equals(dataInfo.DataName, StringComparison.OrdinalIgnoreCase))
                {
                    return item;
                }
                else if (item.Name.Equals(dataInfo.DataName.Replace("_", ""), StringComparison.OrdinalIgnoreCase))
                {
                    return item;
                }
            }
            return null;
        }

        /// 
        /// Return type conversion function.
        /// 
        public MethodInfo FindConvertMethod(Type csharpType, Type dbType)
        {
            if (GetUnderlyingType(dbType) == typeof(bool) || GetUnderlyingType(csharpType) == typeof(bool))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToBooleanMethod : MemberMapperMethod.ToBooleanNullableMethod;
            }
            if (GetUnderlyingType(csharpType).IsEnum)
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToEnumMethod.MakeGenericMethod(csharpType) : MemberMapperMethod.ToEnumNullableMethod.MakeGenericMethod(GetUnderlyingType(csharpType));
            }
            if (GetUnderlyingType(dbType) == typeof(char) || GetUnderlyingType(csharpType) == typeof(char))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToCharMethod : MemberMapperMethod.ToCharNullableMethod;
            }
            if (csharpType == typeof(string))
            {
                return MemberMapperMethod.ToStringMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(Guid) || GetUnderlyingType(csharpType) == typeof(Guid))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToGuidMethod : MemberMapperMethod.ToGuidNullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(DateTime) || GetUnderlyingType(csharpType) == typeof(DateTime))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToDateTimeMethod : MemberMapperMethod.ToDateTimeNullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(byte) || GetUnderlyingType(dbType) == typeof(sbyte) || GetUnderlyingType(csharpType) == typeof(byte) || GetUnderlyingType(csharpType) == typeof(sbyte))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToByteMethod : MemberMapperMethod.ToByteNullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(short) || GetUnderlyingType(dbType) == typeof(ushort) || GetUnderlyingType(csharpType) == typeof(short) || GetUnderlyingType(csharpType) == typeof(ushort))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToIn16Method : MemberMapperMethod.ToIn16NullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(int) || GetUnderlyingType(dbType) == typeof(uint) || GetUnderlyingType(csharpType) == typeof(int) || GetUnderlyingType(csharpType) == typeof(uint))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToIn32Method : MemberMapperMethod.ToIn32NullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(dbType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(long) || GetUnderlyingType(csharpType) == typeof(ulong))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToIn64Method : MemberMapperMethod.ToIn64NullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(float) || GetUnderlyingType(csharpType) == typeof(float))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToFloatMethod : MemberMapperMethod.ToFloatNullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(double) || GetUnderlyingType(csharpType) == typeof(double))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToDoubleMethod : MemberMapperMethod.ToDoubleNullableMethod;
            }
            if (GetUnderlyingType(dbType) == typeof(decimal) || GetUnderlyingType(csharpType) == typeof(decimal))
            {
                return !IsNullableType(csharpType) ? MemberMapperMethod.ToDecimalMethod : MemberMapperMethod.ToDecimalNullableMethod;
            }
            return !IsNullableType(csharpType) ? MemberMapperMethod.ToObjectMethod.MakeGenericMethod(csharpType) : MemberMapperMethod.ToObjectNullableMethod.MakeGenericMethod(Nullable.GetUnderlyingType(GetUnderlyingType(csharpType)));
        }

        private Type GetUnderlyingType(Type type)
        {
            var underlyingType = Nullable.GetUnderlyingType(type);
            return underlyingType ?? type;
        }

        private bool IsNullableType(Type type)
        {
            if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
            {
                return false;
            }
            return true;
        }

        /// 
        /// 数据库类型到Csharp类型转换器
        /// 
        static clast MemberMapperMethod
        {
            #region Method Field
            public static MethodInfo ToObjectMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToObject));
            public static MethodInfo ToByteMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToByte));
            public static MethodInfo ToIn16Method = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt16));
            public static MethodInfo ToIn32Method = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt32));
            public static MethodInfo ToIn64Method = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt64));
            public static MethodInfo ToFloatMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToFloat));
            public static MethodInfo ToDoubleMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDouble));
            public static MethodInfo ToDecimalMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDecimal));
            public static MethodInfo ToBooleanMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToBoolean));
            public static MethodInfo ToCharMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToChar));
            public static MethodInfo ToStringMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToString));
            public static MethodInfo ToDateTimeMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDateTime));
            public static MethodInfo ToEnumMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToEnum));
            public static MethodInfo ToGuidMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToGuid));
            #endregion

            #region NullableMethod Field
            public static MethodInfo ToObjectNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertObjectNullable));
            public static MethodInfo ToByteNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToByteNullable));
            public static MethodInfo ToIn16NullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt16Nullable));
            public static MethodInfo ToIn32NullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt32Nullable));
            public static MethodInfo ToIn64NullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToInt64Nullable));
            public static MethodInfo ToFloatNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToFloatNullable));
            public static MethodInfo ToDoubleNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDoubleNullable));
            public static MethodInfo ToBooleanNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToBooleanNullable));
            public static MethodInfo ToDecimalNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDecimalNullable));
            public static MethodInfo ToCharNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToCharNullable));
            public static MethodInfo ToDateTimeNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToDateTimeNullable));
            public static MethodInfo ToEnumNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToEnumNullable));
            public static MethodInfo ToGuidNullableMethod = typeof(MemberMapperMethod).GetMethod(nameof(MemberMapperMethod.ConvertToGuidNullable));
            #endregion

            #region Define Convert
            public static object ConvertToObject(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetValue(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static byte ConvertToByte(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    var result = dr.GetByte(i);
                    return result;
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static short ConvertToInt16(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetInt16(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static int ConvertToInt32(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetInt32(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static long ConvertToInt64(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetInt64(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static float ConvertToFloat(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetFloat(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static double ConvertToDouble(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetDouble(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static bool ConvertToBoolean(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetBoolean(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static decimal ConvertToDecimal(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetDecimal(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static char ConvertToChar(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    var result = dr.GetChar(i);
                    return result;
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static string ConvertToString(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetString(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static DateTime ConvertToDateTime(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    return dr.GetDateTime(i);
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static T ConvertToEnum(IDataRecord dr, int i) where T : struct
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    var value = dr.GetValue(i);
                    if (Enum.TryParse(value.ToString(), out T result)) return result;
                    return default;
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            public static Guid ConvertToGuid(IDataRecord dr, int i)
            {
                try
                {
                    if (dr.IsDBNull(i))
                    {
                        return default;
                    }
                    var result = dr.GetGuid(i);
                    return result;
                }
                catch
                {
                    throw ThrowException(dr, i);
                }
            }

            private static Exception ThrowException(IDataRecord dr, int i)
            {
                var inner = new FormatException($"Column of {dr.GetName(i)} {dr.GetFieldType(i)} '{dr.GetValue(i)}' was not recognized as a valid {typeof(T).Name}.");
                return new InvalidCastException($"Unable to cast object of type '{dr.GetFieldType(i).Name}' to type '{typeof(int).Name}'.", inner);
            }
            #endregion

            #region Define Nullable Convert
            public static object ConvertObjectNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToObject(dr, i);
            }
            public static byte? ConvertToByteNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToByte(dr, i);
            }
            public static short? ConvertToInt16Nullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToInt16(dr, i);
            }
            public static int? ConvertToInt32Nullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToInt32(dr, i);
            }
            public static long? ConvertToInt64Nullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToInt64(dr, i);
            }
            public static float? ConvertToFloatNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToFloat(dr, i);
            }
            public static double? ConvertToDoubleNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToDouble(dr, i);
            }
            public static bool? ConvertToBooleanNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToBoolean(dr, i);
            }
            public static decimal? ConvertToDecimalNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToDecimal(dr, i);
            }
            public static char? ConvertToCharNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToChar(dr, i);
            }
            public static DateTime? ConvertToDateTimeNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToDateTime(dr, i);
            }
            public static T? ConvertToEnumNullable(IDataRecord dr, int i) where T : struct
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToEnum(dr, i);
            }
            public static Guid? ConvertToGuidNullable(IDataRecord dr, int i)
            {
                if (dr.IsDBNull(i))
                {
                    return default;
                }
                return ConvertToGuid(dr, i);
            }
            #endregion
        }
    }

}