• Dapper源码学习和源码修改(下篇)


    目录:

    Dapper源码学习和源码修改(上篇主要讲解入参解析)

    Dapper源码学习和源码修改(下篇主要讲解出参解析)

    继上篇讲了下自己学习Dapper的心得之后,下篇也随之而来,上篇主要讲的入参解析那下篇自然主打出参映射了。

    好了,废话不多说,开始吧。

    学习之前你的先学习怎么使用Dapper,这个我在上篇都提过,如果没使用过Dapper的同学,先去看看怎么使用吧,我这也简单贴一部分代码吧。

    使用查询的Demo

                    //查询
                    sql = "select * from Teacher";
                    var list = SqlMapper.Query<Teacher>(conn, sql, null).ToList();
    
    
                    sql = "select * from Teacher left join Student on Teacher.Id=Student.Tid";
                    //一对一
                    var list1 = SqlMapper.Query<Teacher, Student, Teacher>(conn, sql,
                        (t, s) =>
                        {
                            if (t.Student == null) t.Student = new List<Student>();
                            t.Student.Add(s);
                            return t;
                        }
                        , null, true, null, "Id", null, null);
                    //一对多
                    Dictionary<string, Teacher> list2Dict = new Dictionary<string, Teacher>();//这个才是最后的结果
                    var list2 = SqlMapper.Query<Teacher, Student, Teacher>(conn, sql,
                        (t, s) =>
                        {
                            Teacher temp;
                            if (!list2Dict.TryGetValue(t.Id, out temp))
                            {
                                temp = t;
                                list2Dict.Add(temp.Id, temp);
                            }
                            if (temp.Student == null) temp.Student = new List<Student>();
                            temp.Student.Add(s);
                            return temp;
                        }
                        , null, true, null, "Id", null, null);

    好了,我也不解释,自己体会。

    我们先看看Dapper提供了哪些对外的查询方法呢,既然是将出参,只有查询才会涉及到DataReader转实体的呢,所以主要就看那几个查询方法就行了。

     

    前两个是一个实体映射的,后面三个是多个实体映射,正常情况下一个实体对应一个表,你也可以一个表对应多个实体也是可行的。

    由浅入深,先看单个实体的查询。

            private static IEnumerable<T> QueryInternal<T>(IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
            {
                var identity = new Identity(sql, commandType, cnn, typeof(T), param == null ? null : param.GetType(), null);
                var info = GetCacheInfo(identity);
    
                using (var cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType))
                {
                    using (var reader = cmd.ExecuteReader())
                    {
                        Func<Func<IDataReader, object>> cacheDeserializer = delegate()
                        {
                            info.Deserializer = GetDeserializer(typeof(T), reader, 0, -1, false);
                            SetQueryCache(identity, info);
                            return info.Deserializer;
                        };
    
                        if (info.Deserializer == null)
                        {
                            cacheDeserializer();
                        }
    
                        var deserializer = info.Deserializer;
    
                        while (reader.Read())
                        {
                            object next;
                            try
                            {
                                next = deserializer(reader);
                            }
                            catch (DataException)
                            {
                                deserializer = cacheDeserializer();
                                next = deserializer(reader);
                            }
                            yield return (T)next;
                        }
                    }
                }
            }

    这个就是单个实体的查询方法,用一张图片说明

    很明显在读取reader的时候  next = deserializer(reader);  就是这个将reader转成实体的,那这个deserializer是什么呢,往上看啊,上面重点二字的地方就是创建deserializer 委托的地方,对了这里插一句这里委托Func(有返回值的泛型委托),之前在入参讲解的时候那里委托是Action(无返回值的泛型委托)。

    也就是说deserializer就是创建委托的地方,我们去看看它的庐山真面目。

            private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
            {
                Func<IDataReader, object> func = null;
                if (IsSimpleValue(type))
                {
                    func = GetSimpleDeserializer(type, startBound);
                }
                else if (typeof(IDictionary).IsAssignableFrom(type))
                {
                    func = GetDictionaryDeserializer(type, startBound);
                }
                else if (type.IsClass)
                {
                    func = GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);
                }
                return func;
            }

     func = GetSimpleDeserializer(type, startBound);   func = GetDictionaryDeserializer(type, startBound);  这两个是我扩展的两种类型,就是为了让出参支持简单类型和继承IDictionary的类型。

    而 func = GetClassDeserializer(type, reader, startBound, length, returnNullIfFirstMissing);  这个才是重点中的难点,这个就是将reader转成实体的委托。

            private static Func<IDataReader, object> GetClassDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
            {
                var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), type, new[] { typeof(IDataReader) }, true);
    
                var il = dm.GetILGenerator();
                il.DeclareLocal(typeof(int));
                il.DeclareLocal(type);
                bool haveEnumLocal = false;
                il.Emit(OpCodes.Ldc_I4_0);
                il.Emit(OpCodes.Stloc_0);
                List<PropertyInfo> properties = GetSettableProps(type);
                List<FieldInfo> fields = GetSettableFields(type);
                if (length == -1)
                {
                    length = reader.FieldCount - startBound;
                }
    
                if (reader.FieldCount <= startBound)
                {
                    throw new ArgumentException("When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id", "splitOn");
                }
    
                var names = new List<string>();
    
                for (int i = startBound; i < startBound + length; i++)
                {
                    names.Add(reader.GetName(i));
                }
                var setters = new List<DynamicSetter>();
    
                foreach (var name in names)
                {
                    PropertyInfo p = FirstOrDefault(properties, new Func<PropertyInfo, bool>(delegate(PropertyInfo pro) { return Equals(pro.Name, name, StringComparison.Ordinal); }))
                        ?? FirstOrDefault(properties, new Func<PropertyInfo, bool>(delegate(PropertyInfo pro) { return Equals(pro.Name, name, StringComparison.OrdinalIgnoreCase); }));
                    FieldInfo f = FirstOrDefault(fields, new Func<FieldInfo, bool>(delegate(FieldInfo fin) { return Equals(fin.Name, name, StringComparison.Ordinal); }))
                        ?? FirstOrDefault(fields, new Func<FieldInfo, bool>(delegate(FieldInfo fin) { return Equals(fin.Name, name, StringComparison.OrdinalIgnoreCase); }));
                    setters.Add(new DynamicSetter { Name = name, Property = p, Field = f });
                }
    
                int index = startBound;
    
                il.BeginExceptionBlock();
                // stack is empty
                il.Emit(OpCodes.Newobj, type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null)); // stack is now [target]
                bool first = true;
                var allDone = il.DefineLabel();
                foreach (var item in setters)
                {
                    if (item.Property != null || item.Field != null)
                    {
                        il.Emit(OpCodes.Dup); // stack is now [target][target]
                        Label isDbNullLabel = il.DefineLabel();
                        Label finishLabel = il.DefineLabel();
    
                        il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
                        EmitInt32(il, index); // stack is now [target][target][reader][index]
                        il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
                        il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]
                        il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]
    
    
                        Type memberType = item.Property != null ? item.Property.PropertyType : item.Field.FieldType;
    
                        if (memberType == typeof(char) || memberType == typeof(char?))
                        {
                            il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(
                                memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
                        }
                        else
                        {
                            il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
                            il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
                            il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]
    
                            // unbox nullable enums as the primitive, i.e. byte etc
    
                            var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
                            var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;
    
                            if (unboxType.IsEnum)
                            {
                                if (!haveEnumLocal)
                                {
                                    il.DeclareLocal(typeof(string));
                                    haveEnumLocal = true;
                                }
    
                                Label isNotString = il.DefineLabel();
                                il.Emit(OpCodes.Dup); // stack is now [target][target][value][value]
                                il.Emit(OpCodes.Isinst, typeof(string)); // stack is now [target][target][value-as-object][string or null]
                                il.Emit(OpCodes.Dup);// stack is now [target][target][value-as-object][string or null][string or null]
                                il.Emit(OpCodes.Stloc_2); // stack is now [target][target][value-as-object][string or null]
                                il.Emit(OpCodes.Brfalse_S, isNotString); // stack is now [target][target][value-as-object]
    
                                il.Emit(OpCodes.Pop); // stack is now [target][target]
    
    
                                il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
                                il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
                                il.Emit(OpCodes.Ldloc_2); // stack is now [target][target][enum-type][string]
                                il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
                                il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]
    
                                il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
    
                                if (nullUnderlyingType != null)
                                {
                                    il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
                                }
                                if (item.Property != null)
                                {
                                    var setter = item.Property.DeclaringType == type ? item.Property.GetSetMethod(true) : item.Property.DeclaringType.GetProperty(item.Property.Name).GetSetMethod(true);
                                    il.Emit(OpCodes.Callvirt, setter); // stack is now [target]
                                }
                                else
                                {
                                    il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
                                }
                                il.Emit(OpCodes.Br_S, finishLabel);
    
    
                                il.MarkLabel(isNotString);
                            }
                            //if (memberType == typeof(System.Data.Linq.Binary))
                            //{
                            //    il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
                            //    il.Emit(OpCodes.Newobj, typeof(System.Data.Linq.Binary).GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
                            //}
                            //else
                            //{
                            il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
                            //}
                            if (nullUnderlyingType != null && nullUnderlyingType.IsEnum)
                            {
                                il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType }));
                            }
                        }
                        if (item.Property != null)
                        {
                            var setter = item.Property.DeclaringType == type ? item.Property.GetSetMethod(true) : item.Property.DeclaringType.GetProperty(item.Property.Name).GetSetMethod(true);
                            il.Emit(OpCodes.Callvirt, setter); // stack is now [target]
                        }
                        else
                        {
                            il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
                        }
    
                        il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]
    
                        il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
    
                        il.Emit(OpCodes.Pop); // stack is now [target][target]
                        il.Emit(OpCodes.Pop); // stack is now [target]
    
                        if (first && returnNullIfFirstMissing)
                        {
                            il.Emit(OpCodes.Pop);
                            il.Emit(OpCodes.Ldnull); // stack is now [null]
                            il.Emit(OpCodes.Stloc_1);
                            il.Emit(OpCodes.Br, allDone);
                        }
    
                        il.MarkLabel(finishLabel);
                    }
                    first = false;
                    index += 1;
                }
                il.Emit(OpCodes.Stloc_1); // stack is empty
                il.MarkLabel(allDone);
                il.BeginCatchBlock(typeof(Exception)); // stack is Exception
                il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
                il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
                il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Stloc_1); // to make it verifiable
                il.EndExceptionBlock();
    
                il.Emit(OpCodes.Ldloc_1); // stack is empty
                il.Emit(OpCodes.Ret);
    
                return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
            }

    呵呵,我也不多说,这个就是Dapper核心价值所在,使用Emit创建实体。

    上面不是说到,我也扩展出参支持两种类型嘛,使用的时候就是这样

    其一:IDictionary

                    sql = "select * from student";// where Id='916a84c6-85cb-4b41-b52a-96a0685d91b5'";
                    var sex = SqlMapper.Query<Dictionary<string, object>>(conn, sql, null).ToList();

    很简单的将结果转成 Dictionary ,它的委托实现如下:

            private static Func<IDataReader, object> GetDictionaryDeserializer(Type type, int index)
            {
                return delegate(IDataReader r)
                {
                    IDictionary ht = Activator.CreateInstance(type) as IDictionary;
                    for (int i = 0; i < r.FieldCount; i++)
                    {
                        ht.Add(r.GetName(i), r[i]);
                    }
                    return ht;
                };
            }

    其二:SimpleValue

    何为SimpleValue呢,看下面

            private static bool IsSimpleValue(Type type)
            {
                if (
                            type.IsEnum ||
                            isSame(type, typeof(byte)) || isSame(type, typeof(byte?)) ||
                            isSame(type, typeof(sbyte)) || isSame(type, typeof(sbyte?)) ||
                            isSame(type, typeof(long)) || isSame(type, typeof(long?)) ||
                            isSame(type, typeof(ulong)) || isSame(type, typeof(ulong?)) ||
                            isSame(type, typeof(short)) || isSame(type, typeof(short?)) ||
                            isSame(type, typeof(ushort)) || isSame(type, typeof(ushort?)) ||
                            isSame(type, typeof(int)) || isSame(type, typeof(int?)) ||
                            isSame(type, typeof(uint)) || isSame(type, typeof(uint?)) ||
                            isSame(type, typeof(float)) || isSame(type, typeof(float?)) ||
                            isSame(type, typeof(double)) || isSame(type, typeof(double?)) ||
                            isSame(type, typeof(decimal)) || isSame(type, typeof(decimal?)) ||
                            isSame(type, typeof(char)) || isSame(type, typeof(char?)) ||
                            isSame(type, typeof(bool)) || isSame(type, typeof(bool?)) ||
                            isSame(type, typeof(DateTime)) || isSame(type, typeof(DateTime?)) ||
                            isSame(type, typeof(string)) || isSame(type, typeof(object))
                )
                    return true;
                else
                    return false;
            }

    如果是上面的类型,我就会用下面的委托来转换reader

            private static Func<IDataReader, object> GetSimpleDeserializer(Type type, int index)
            {
                return delegate(IDataReader r)
                {
                    object obj = r.GetValue(index);
                    if (obj == null || (obj is DBNull)) return type.IsValueType ? Activator.CreateInstance(type) : null;
                    else
                    {
                        if (type.IsEnum)
                            obj = Convert.ChangeType(obj, typeof(int));
                        else
                            obj = Convert.ChangeType(obj, type);
                        return obj;
                    }
                };
            }

    后续,上面只是讲到单个实体查询的情况,至于多个实体的查询,只需理解两个参数

    public static IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(IDbConnection cnn, string sql, Func<TFirst, TSecond, TReturn> map, object param, bool buffered, IDbTransaction transaction, string splitOn, int? commandTimeout, CommandType? commandType)

    这两个  splitOn  和  map  如果理解了这两个参数,其他跟单个实体是一样,至于这两个参数我先不讲解,后面我会提供源码,自己看,或者以后抽空我再讲讲。

    总结:

    出参就讲到这吧,后续我看讲不讲Dapper扩展的,会提供源码下载,源码可能和文章会有些出入,发布文章后我修改过源码。

    源码下载:

    点我点我点我

  • 相关阅读:
    springmvc上传下载
    在线剪切
    Spring入门
    登录验证后端添加过滤器
    SpringMVC入门
    ScriptManager 以及ScriptManagerProxy在母版中使用和Service属性的使用
    GridView中如何使用CommandField删除时,弹出确认框?
    C# 测量用指定的 Font 绘制的指定字符串。(指定字符串的像素长度)
    ASP.NET AJAX 简单地过一下每个控件(ScriptManager、ScriptManagerProxy、UpdatePanel、 UpdateProgress和Timer)
    XML结构和数据导入到SQL server
  • 原文地址:https://www.cnblogs.com/deeround/p/6633611.html
Copyright © 2020-2023  润新知