【手撸一个ORM】第七步、SqlDataReader转实体

因为实体查询和按需查询(Select)的解析方式有些许不同,所以这里分为两个类来实现。

using System;

namespace MyOrm.Mappers
{
    public class IncludePropertySdrMap
    {
        public Type Type { get; set; }

        public string PropertyName { get; set; }

        public int Index { get; set; }
    }
}

用于实体查询的转换类

using MyOrm.Reflections;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;

namespace MyOrm.Mappers
{
    public class SqlDataReaderConverter<T> where T : class, new()
    {
        private static readonly Dictionary<string, Func<SqlDataReader, T>> Dict
            = new Dictionary<string, Func<SqlDataReader, T>>();

        public MyEntity Master { get; set; }

        public List<string> Includes { get; set; }

        public string Key { get; set; }

        public SqlDataReaderConverter(string[] props = null)
        {
            Master = MyEntityContainer.Get(typeof(T));

            if (props == null || props.Length == 0)
            {
                Includes = new List<string>();
                Key = typeof(T).Name;
            }
            else
            {
                Includes = props.ToList();
                Key = typeof(T).Name + "-" + string.Join("-", props.OrderBy(p => p).Distinct());
            }
        }


        #region 反射
        public T ConvertToEntity(SqlDataReader sdr)
        {
            var entity = new T();

            foreach (var property in Master.Properties)
            {
                property.PropertyInfo.SetValue(entity, sdr[property.Name]);
            }

            foreach (var include in Includes)
            {
                var prop = Master.Properties.Single(p => p.Name == include);
                if (prop != null)
                {
                    var subType = prop.PropertyInfo.PropertyType;
                    var subEntityInfo = MyEntityContainer.Get(subType);
                    var subEntity = Activator.CreateInstance(subType);

                    foreach (var subProperty in subEntityInfo.Properties)
                    {
                        if (subProperty.IsMap)
                        {
                            subProperty.PropertyInfo.SetValue(subEntity, sdr[$"{include}_{subProperty.Name}"]);
                        }
                    }

                    prop.PropertyInfo.SetValue(entity, subEntity);
                }
            }

            return entity;
        }
        #endregion

        #region 表达式目录树
        public Func<SqlDataReader, T> GetFunc(SqlDataReader sdr)
        {
            if (!Dict.TryGetValue(Key, out var func))
            {
                var sdrParameter = Expression.Parameter(typeof(SqlDataReader), "sdr");
                var memberBindings = new List<MemberBinding>();
                var subMemberMaps = new Dictionary<string, List<IncludePropertySdrMap>>();
                foreach (var include in Includes)
                {
                    subMemberMaps.Add(include, new List<IncludePropertySdrMap>());
                }

                for (var i = 0; i < sdr.FieldCount; i++)
                {
                    var fieldName = sdr.GetName(i);
                    var fieldNames = fieldName.Split('_');

                    if (fieldNames.Length == 1)
                    {
                        var property = Master.Properties.Single(p => p.Name == fieldName);
                        if (property != null)
                        {
                            var methodName = GetSdrMethodName(property.PropertyInfo.PropertyType);
                            var methodCall = Expression.Call(sdrParameter,
                                typeof(SqlDataReader).GetMethod(methodName) ?? throw new InvalidOperationException(),
                                Expression.Constant(i));

                            Expression setValueExpression;
                            if (property.PropertyInfo.PropertyType.IsGenericType &&
                                property.PropertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                            {
                                setValueExpression = Expression.Convert(methodCall, property.PropertyInfo.PropertyType);
                            }
                            else
                            {
                                setValueExpression = methodCall;
                            }

                            //memberBindings.Add(Expression.Bind(property.PropertyInfo, methodCall));
                            memberBindings.Add(
                                Expression.Bind(
                                    property.PropertyInfo,
                                    Expression.Condition(
                                        Expression.TypeIs(
                                            Expression.Call(
                                                sdrParameter,
                                                typeof(SqlDataReader).GetMethod("get_Item", new[] {typeof(int)}) ??
                                                throw new InvalidOperationException(),
                                                Expression.Constant(i)),
                                            typeof(DBNull)
                                        ),
                                        Expression.Default(property.PropertyInfo.PropertyType),
                                        setValueExpression
                                    )
                                )
                            );
                        }
                    }
                    else
                    {
                        if (subMemberMaps.TryGetValue(fieldNames[0], out var list))
                        {
                            list.Add(new IncludePropertySdrMap { PropertyName = fieldNames[1], Index = i });
                        }
                    }
                }

                foreach (var include in subMemberMaps)
                {
                    var prop = Master.Properties.Single(p => p.Name == include.Key);
                    if (prop != null)
                    {
                        var subEntityInfo = MyEntityContainer.Get(prop.PropertyInfo.PropertyType);
                        var subBindingList = new List<MemberBinding>();
                        foreach (var subProperty in subEntityInfo.Properties)
                        {
                            if (subProperty.IsMap)
                            {
                                var mapper = include.Value.SingleOrDefault(v => v.PropertyName == subProperty.Name);
                                if (mapper != null)
                                {
                                    var methodName = GetSdrMethodName(subProperty.PropertyInfo.PropertyType);
                                    var methodCall = Expression.Call(
                                        sdrParameter,
                                        typeof(SqlDataReader).GetMethod(methodName) ??
                                        throw new InvalidOperationException(),
                                        Expression.Constant(mapper.Index));

                                    Expression setValueExpression;
                                    if (subProperty.PropertyInfo.PropertyType.IsGenericType &&
                                        subProperty.PropertyInfo.PropertyType.GetGenericTypeDefinition() ==
                                        typeof(Nullable<>))
                                    {
                                        setValueExpression = Expression.Convert(methodCall,
                                            subProperty.PropertyInfo.PropertyType);
                                    }
                                    else
                                    {
                                        setValueExpression = methodCall;
                                    }

                                    subBindingList.Add(
                                        Expression.Bind(
                                            subProperty.PropertyInfo,
                                            Expression.Condition(
                                                Expression.TypeIs(
                                                    Expression.Call(
                                                        sdrParameter,
                                                        typeof(SqlDataReader).GetMethod("get_Item",
                                                            new[] {typeof(int)}) ??
                                                        throw new InvalidOperationException(),
                                                        Expression.Constant(mapper.Index)),
                                                    typeof(DBNull)
                                                ),
                                                Expression.Default(subProperty.PropertyInfo.PropertyType),
                                                setValueExpression
                                            )
                                        )
                                    );
                                }
                            }

                            var subInitExpression = Expression.MemberInit(
                                Expression.New(prop.PropertyInfo.PropertyType),
                                subBindingList);
                            memberBindings.Add(Expression.Bind(prop.PropertyInfo, subInitExpression));
                        }
                    }
                }

                var initExpression = Expression.MemberInit(Expression.New(typeof(T)), memberBindings);
                func = Expression.Lambda<Func<SqlDataReader, T>>(initExpression, sdrParameter).Compile();
                Dict.Add(Key, func);
            }
            else
            {
                //Console.WriteLine("应用了缓存");
            }
            return func;
        }

        public T ConvertToEntity2(SqlDataReader sdr)
        {
            if (sdr.HasRows)
            {
                var func = GetFunc(sdr);
                if (sdr.Read())
                {
                    return func.Invoke(sdr);
                }
            }
            return default(T);
        }

        public List<T> ConvertToEntityList(SqlDataReader sdr)
        {
            var result = new List<T>();
            if (!sdr.HasRows)
            {
                return result;
            }

            var func = GetFunc(sdr);
            do
            {
                while (sdr.Read())
                {
                    result.Add(func(sdr));
                }
            } while (sdr.NextResult());
            

            return result;
        }

        public List<T> ConvertToEntityList2(SqlDataReader sdr)
        {
            var result = new List<T>();
            while (sdr.Read())
            {
                result.Add(ConvertToEntity2(sdr));
            }
            return result;
        }

        /// <summary>
        /// 获取SqlDataReader转实体属性时调用的方法名
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private string GetSdrMethodName(Type type)
        {
            var realType = GetRealType(type);
            string methodName;

            if (realType == typeof(string))
            {
                methodName = "GetString";
            }
            else if (realType == typeof(int))
            {
                methodName = "GetInt32";
            }
            else if (realType == typeof(DateTime))
            {
                methodName = "GetDateTime";
            }
            else if (realType == typeof(decimal))
            {
                methodName = "GetDecimal";
            }
            else if (realType == typeof(Guid))
            {
                methodName = "GetGuid";
            }
            else if (realType == typeof(bool))
            {
                methodName = "GetBoolean";
            }
            else
            {
                throw new ArgumentException($"不受支持的类型:{type.FullName}");
            }

            return methodName;
        }

        private static Type GetRealType(Type type)
        {
            var realType = type.IsGenericType &&
                           type.GetGenericTypeDefinition() == typeof(Nullable<>)
                ? type.GetGenericArguments()[0]
                : type;

            return realType;
        }
        #endregion
    }
}

用于按需查询的转换类

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using MyOrm.Expressions;
using MyOrm.Reflections;

namespace MyOrm.Mappers
{
    public class SqlDataReaderMapper
    {
        public Func<SqlDataReader, TTarget> ResolveClass<TTarget>(SqlDataReader sdr)
        {
            if (!sdr.HasRows) return null;

            var sdrParameter = Expression.Parameter(typeof(SqlDataReader), "sdr");
            var memberBindings = new List<MemberBinding>();
            var subMemberMaps = new Dictionary<string, List<IncludePropertySdrMap>>();

            var masterEntity = MyEntityContainer.Get(typeof(TTarget));


            for (var i = 0; i < sdr.FieldCount; i++)
            {
                var fieldName = sdr.GetName(i);
                var fieldNames = fieldName.Split("__");

                if (fieldNames.Length == 1)
                {
                    var property = masterEntity.Properties.Single(p => p.Name == fieldName);
                    if (property != null)
                    {
                        var methodName = GetSdrMethodName(property.PropertyInfo.PropertyType);
                        var methodCall = Expression.Call(sdrParameter,
                            typeof(SqlDataReader).GetMethod(methodName) ?? throw new InvalidOperationException(),
                            Expression.Constant(i));

                        Expression setValueExpression;
                        if (property.PropertyInfo.PropertyType.IsGenericType &&
                            property.PropertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            setValueExpression = Expression.Convert(methodCall, property.PropertyInfo.PropertyType);
                        }
                        else
                        {
                            setValueExpression = methodCall;
                        }

                        //memberBindings.Add(Expression.Bind(property.PropertyInfo, methodCall));
                        memberBindings.Add(
                            Expression.Bind(
                                property.PropertyInfo,
                                Expression.Condition(
                                    Expression.TypeIs(
                                        Expression.Call(
                                            sdrParameter,
                                            typeof(SqlDataReader).GetMethod("get_Item", new[] { typeof(int) }) ??
                                            throw new InvalidOperationException(),
                                            Expression.Constant(i)),
                                        typeof(DBNull)
                                    ),
                                    Expression.Default(property.PropertyInfo.PropertyType),
                                    setValueExpression
                                )
                            )
                        );
                    }
                }
                else
                {
                    if (subMemberMaps.TryGetValue(fieldNames[0], out var list))
                    {
                        list.Add(new IncludePropertySdrMap { PropertyName = fieldNames[1], Index = i });
                    }
                }
            }

            foreach (var include in subMemberMaps)
            {
                var prop = masterEntity.Properties.Single(p => p.Name == include.Key);
                if (prop != null)
                {
                    var subEntityInfo = MyEntityContainer.Get(prop.PropertyInfo.PropertyType);
                    var subBindingList = new List<MemberBinding>();
                    foreach (var subProperty in subEntityInfo.Properties)
                    {
                        if (subProperty.IsMap)
                        {
                            var mapper = include.Value.SingleOrDefault(v => v.PropertyName == subProperty.Name);
                            if (mapper != null)
                            {
                                var methodName = GetSdrMethodName(subProperty.PropertyInfo.PropertyType);
                                var methodCall = Expression.Call(
                                    sdrParameter,
                                    typeof(SqlDataReader).GetMethod(methodName) ??
                                    throw new InvalidOperationException(),
                                    Expression.Constant(mapper.Index));

                                Expression setValueExpression;
                                if (subProperty.PropertyInfo.PropertyType.IsGenericType &&
                                    subProperty.PropertyInfo.PropertyType.GetGenericTypeDefinition() ==
                                    typeof(Nullable<>))
                                {
                                    setValueExpression = Expression.Convert(methodCall,
                                        subProperty.PropertyInfo.PropertyType);
                                }
                                else
                                {
                                    setValueExpression = methodCall;
                                }

                                subBindingList.Add(
                                    Expression.Bind(
                                        subProperty.PropertyInfo,
                                        Expression.Condition(
                                            Expression.TypeIs(
                                                Expression.Call(
                                                    sdrParameter,
                                                    typeof(SqlDataReader).GetMethod("get_Item",
                                                        new[] { typeof(int) }) ??
                                                    throw new InvalidOperationException(),
                                                    Expression.Constant(mapper.Index)),
                                                typeof(DBNull)
                                            ),
                                            Expression.Default(subProperty.PropertyInfo.PropertyType),
                                            setValueExpression
                                        )
                                    )
                                );
                            }
                        }

                        var subInitExpression = Expression.MemberInit(
                            Expression.New(prop.PropertyInfo.PropertyType),
                            subBindingList);
                        memberBindings.Add(Expression.Bind(prop.PropertyInfo, subInitExpression));
                    }
                }
            }

            var initExpression = Expression.MemberInit(Expression.New(typeof(TTarget)), memberBindings);
            return Expression.Lambda<Func<SqlDataReader, TTarget>>(initExpression, sdrParameter).Compile();
        }

        public Func<SqlDataReader, TTarget> ResolveConstant<TTarget>(SqlDataReader sdr, string fieldName = "")
        {
            var type = typeof(TTarget);
            var sdrParameter = Expression.Parameter(typeof(SqlDataReader), "sdr");
            MethodCallExpression callExpression;
            if (string.IsNullOrWhiteSpace(fieldName))
            {
                var methodName = GetSdrMethodName(type);
                callExpression = Expression.Call(sdrParameter, typeof(SqlDataReader).GetMethod(methodName),
                    Expression.Constant(0));
                return Expression.Lambda<Func<SqlDataReader, TTarget>>(callExpression, sdrParameter).Compile();
            }
            else
            {
                callExpression = Expression.Call(sdrParameter,
                    typeof(SqlDataReader).GetMethod("get_item", new[] {typeof(string)}),
                    Expression.Constant(fieldName));
                var convertExpression = Expression.Convert(callExpression, type);
                return Expression.Lambda<Func<SqlDataReader, TTarget>>(convertExpression, sdrParameter).Compile();
            }
        }

        public Func<SqlDataReader, dynamic> Resolve2(SqlDataReader sdr)
        {
            var sdrParameter = Expression.Parameter(typeof(SqlDataReader), "sdr");
            var newExpression = Expression.New(typeof(System.Dynamic.ExpandoObject));
            var convertExpression = Expression.Convert(newExpression, typeof(IDictionary<string, object>));

            var memberBindings = new List<MemberBinding>();
            for(var i = 0; i < sdr.FieldCount; i++)
            {
                var nameExpression = Expression.Call(sdrParameter, typeof(SqlDataReader).GetMethod("GetName"), Expression.Constant(i));
                //var itemExpression = Expression.Call(
                //            sdrParameter,
                //            typeof(SqlDataReader).GetMethod("get_Item",
                //                new[] { typeof(int) }) ??
                //            throw new InvalidOperationException(),
                //            Expression.Constant(i));
                //var type = Expression.Constant(Expression.Call(sdrParameter, typeof(SqlDataReader).GetMethod("GetFieldType", new[] { typeof(int) }), Expression.Constant(i)));
                var valueExpression = Expression.Call(sdrParameter, typeof(SqlDataReader).GetMethod("GetValue", new[] { typeof(int) }), Expression.Constant(i));

                //var callExpression = Expression.Call(
                //    convertExpression,
                //    typeof(IDictionary<string, object>).GetMethod("Add"),
                //    nameExpression, valueExpression);

                Expression.Call(newExpression,
                    typeof(System.Dynamic.ExpandoObject).GetMethod("TryAdd", new[] { typeof(string), typeof(object) }),
                    nameExpression,
                    valueExpression);
            }
            var initExpression = Expression.MemberInit(newExpression);
            var lambda = Expression.Lambda<Func<SqlDataReader, dynamic>>(initExpression, sdrParameter);
            return lambda.Compile();
        }

        public List<T> ConvertToList<T>(SqlDataReader sdr)
        {
            var result = new List<T>();
            if (!sdr.HasRows)
            {
                return result;
            }

            var func = typeof(T).IsClass && typeof(T) != typeof(string) ? ResolveClass<T>(sdr) : ResolveConstant<T>(sdr);

            if (func == null)
            {
                return result;
            }

            while (sdr.Read())
            {
                result.Add(func.Invoke(sdr));
            }

            return result;
        }

        public T ConvertToEntity<T>(SqlDataReader sdr)
        {
            if (!sdr.HasRows)
            {
                return default(T);
            }

            var func = typeof(T).IsClass ? ResolveClass<T>(sdr) : ResolveConstant<T>(sdr);

            if (func == null)
            {
                return default(T);
            }

            if (sdr.Read())
            {
                return func.Invoke(sdr);
            }

            return default(T);
        }

        public List<dynamic> ConvertToList(SqlDataReader sdr)
        {
            var result = new List<dynamic>();
            if (!sdr.HasRows)
            {
                return result;
            }

            var func = Resolve2(sdr);

            if (func == null)
            {
                return result;
            }

            while (sdr.Read())
            {
                result.Add(func.Invoke(sdr));
            }

            return result;
        }

        public dynamic ConvertToEntity(SqlDataReader sdr)
        {
            if (!sdr.HasRows)
            {
                return null;
            }

            var func = Resolve2(sdr);

            if (func != null && sdr.Read())
            {
                return func.Invoke(sdr);
            }

            return null;
        }

        private string GetSdrMethodName(Type type)
        {
            var realType = GetRealType(type);
            string methodName;

            if (realType == typeof(string))
            {
                methodName = "GetString";
            }
            else if (realType == typeof(int))
            {
                methodName = "GetInt32";
            }
            else if (realType == typeof(DateTime))
            {
                methodName = "GetDateTime";
            }
            else if (realType == typeof(decimal))
            {
                methodName = "GetDecimal";
            }
            else if (realType == typeof(Guid))
            {
                methodName = "GetGuid";
            }
            else if (realType == typeof(bool))
            {
                methodName = "GetBoolean";
            }
            else
            {
                throw new ArgumentException($"不受支持的类型:{type.FullName}");
            }

            return methodName;
        }

        public Type ConvertSdrFieldToType(SqlDataReader sdr)
        {
            return null;
        }

        private static Type GetRealType(Type type)
        {
            var realType = type.IsGenericType &&
                           type.GetGenericTypeDefinition() == typeof(Nullable<>)
                ? type.GetGenericArguments()[0]
                : type;

            return realType;
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/diwu0510/p/10663477.html