Emit学习(4) - Dapper解析之数据对象映射(一)

感觉好久没有写博客了, 这几天有点小忙, 接下来会更忙, 索性就先写一篇吧. 后面估计会有更长的一段时间不会更新博客了.

废话不多说, 先上菜.

一、示例

1. 先建类, 类的名称与读取的表名并没有什么关系,可以不一样, 然后就是其中的属性大小写不限

public class Tch_Teacher
    {
        public int Id { get; set; }
        
        public string Name { get; set; }

        public bool IsDoublePosition { get; set; }

        public DateTime CreateDate { get; set; }
    }

    public class Test : Tch_Teacher //, ISupportInitialize  此接口有两个方法, BeginInit, EndInit
    {
        //[ExplicitConstructor] Dapper会优先查找设置了此属性的构造函数
        //public Test() {  }  

        public string BId { get; set; }

        //public void BeginInit()
        //{
        //    Console.WriteLine("Test BeginInit");
        //}

        //public void EndInit()
        //{
        //    Console.WriteLine("Test EndInit");
        //}
    }

2. 测试代码

    class Program
    {
        static void Main(string[] args)
        {
            var conStr = ConfigurationManager.ConnectionStrings["ConStr"].ToString();
            using (IDbConnection conn = new MySqlConnection(conStr))
            {
                var sql = "select Count(1) from tch_teacher where id>@Id limit 3;";
                //Console.WriteLine(conn.Query<int?>(sql, new { Id = 10 })); //error
                Console.WriteLine(conn.Query<int>(sql, new { Id = 10 }).FirstOrDefault());   // 19
                Console.WriteLine(conn.Query<string>(sql, new { Id = 1 }).FirstOrDefault()); // 19

                sql = "select Id, BId, No, Name, CreateDate from tch_teacher limit 3;";  //No这个字段, 在类中并没有
                var list = conn.Query<Test>(sql);
                Console.WriteLine(list.ToList().FirstOrDefault().BId);  // c5f5959e-0744-42cd-a843-145e28149d9b
            }
            Console.ReadKey();
        }
    }
   

接下来, 可以进入Dapper的部分了

三、Dapper 开始

Query<int/string> 和 Query<Test>在读取数据的部分是一样的, 开始出现不同的地方主要体现在 object to model 的部分,

 private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)

 

首先注意一个地方
static SqlMapper()
        {
        //这部分是 简单类型处理用到的, 当然这其中并不仅仅只有简单类型 typeMap
= new Dictionary<Type, DbType>(); typeMap[typeof(byte)] = DbType.Byte; typeMap[typeof(sbyte)] = DbType.SByte; typeMap[typeof(short)] = DbType.Int16; typeMap[typeof(ushort)] = DbType.UInt16; typeMap[typeof(int)] = DbType.Int32; typeMap[typeof(uint)] = DbType.UInt32; typeMap[typeof(long)] = DbType.Int64; typeMap[typeof(ulong)] = DbType.UInt64; typeMap[typeof(float)] = DbType.Single; typeMap[typeof(double)] = DbType.Double; typeMap[typeof(decimal)] = DbType.Decimal; typeMap[typeof(bool)] = DbType.Boolean; typeMap[typeof(string)] = DbType.String; typeMap[typeof(char)] = DbType.StringFixedLength; typeMap[typeof(Guid)] = DbType.Guid; typeMap[typeof(DateTime)] = DbType.DateTime; typeMap[typeof(DateTimeOffset)] = DbType.DateTimeOffset; typeMap[typeof(TimeSpan)] = DbType.Time; typeMap[typeof(byte[])] = DbType.Binary; typeMap[typeof(byte?)] = DbType.Byte; typeMap[typeof(sbyte?)] = DbType.SByte; typeMap[typeof(short?)] = DbType.Int16; typeMap[typeof(ushort?)] = DbType.UInt16; typeMap[typeof(int?)] = DbType.Int32; typeMap[typeof(uint?)] = DbType.UInt32; typeMap[typeof(long?)] = DbType.Int64; typeMap[typeof(ulong?)] = DbType.UInt64; typeMap[typeof(float?)] = DbType.Single; typeMap[typeof(double?)] = DbType.Double; typeMap[typeof(decimal?)] = DbType.Decimal; typeMap[typeof(bool?)] = DbType.Boolean; typeMap[typeof(char?)] = DbType.StringFixedLength; typeMap[typeof(Guid?)] = DbType.Guid; typeMap[typeof(DateTime?)] = DbType.DateTime; typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset; typeMap[typeof(TimeSpan?)] = DbType.Time; typeMap[typeof(object)] = DbType.Object;         
        //这个方法可以实现自定义处理, 它是一个public static 方法             AddTypeHandlerImpl(
typeof(DataTable), new DataTableHandler(), false); }

 

然后看GetDeserializer方法

private static Func<IDataReader, object> GetDeserializer(Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing)
        {

            // dynamic is passed in as Object ... by c# design
            if (type == typeof(object)
                || type == typeof(DapperRow))
            {
          //object / dynamic 类型, 会执行以下方法
return GetDapperRowDeserializer(reader, startBound, length, returnNullIfFirstMissing); } Type underlyingType = null; if (!(typeMap.ContainsKey(type) || type.IsEnum || type.FullName == LinqBinary || (type.IsValueType && (underlyingType = Nullable.GetUnderlyingType(type)) != null && underlyingType.IsEnum))) { ITypeHandler handler; if (typeHandlers.TryGetValue(type, out handler)) { //自定义处理 return GetHandlerDeserializer(handler, type, startBound); } //复杂类型的处理 return GetTypeDeserializer(type, reader, startBound, length, returnNullIfFirstMissing); } //以上简单类型, 值类型, 可空值类型, 枚举, linq的二进制 的处理 return GetStructDeserializer(type, underlyingType ?? type, startBound); }

这里我只介绍 复杂类型的处理方式了, 至于其他的, 跟Emit的主题关系不是很大, 有兴趣的童鞋, 可以自己去看一下, 应该是能看懂的

由于 GetTypeDeserializer 这个方法实在是太长了, 我把说明都写在注释里面去吧.  按照我的注释, 应该是能看懂整个过程的. 可能还是IL那一段不太好懂, 我第一次看的时候, 就看到那里就没继续看下去了, 实在是不想继续看了. 以下是代码部分

  1 /// <summary>
  2         /// Internal use only
  3         /// </summary>
  4         /// <param name="type"></param>
  5         /// <param name="reader"></param>
  6         /// <param name="startBound"></param>
  7         /// <param name="length"></param>
  8         /// <param name="returnNullIfFirstMissing"></param>
  9         /// <returns></returns>
 10         public static Func<IDataReader, object> GetTypeDeserializer(
 11 #if CSHARP30
 12 Type type, IDataReader reader, int startBound, int length, bool returnNullIfFirstMissing
 13 #else
 14 Type type, IDataReader reader, int startBound = 0, int length = -1, bool returnNullIfFirstMissing = false
 15 #endif
 16 )
 17         {
 18             //创建动态方法 Deserialize[Guid]
 19             var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(IDataReader) }, true);
 20             var il = dm.GetILGenerator();
 21             il.DeclareLocal(typeof(int));  //定义本地变量 loc0
 22             il.DeclareLocal(type);        //定义本地变量 loc1 -> target
 23             il.Emit(OpCodes.Ldc_I4_0);
 24             il.Emit(OpCodes.Stloc_0);   //初始化本地变量loc0, loc0 = 0  
 25 
 26             if (length == -1)
 27             {
 28                 length = reader.FieldCount - startBound; //获取要转换字段的个数
 29             }
 30 
 31             if (reader.FieldCount <= startBound)
 32             {
 33                 throw MultiMapException(reader);
 34             }
 35 
 36             //获取读取出来的字段名, 并转入数组中   -> string[]  Id, BId, No, Name, CreateDate
 37             var names = Enumerable.Range(startBound, length).Select(i => reader.GetName(i)).ToArray(); 
 38 
 39             ITypeMap typeMap = GetTypeMap(type);  //new DefaultTypeMap(type)
 40 
 41             int index = startBound;
 42 
 43             //有参构造函数
 44             ConstructorInfo specializedConstructor = null;
 45             //需要初始化标志
 46             bool supportInitialize = false;
 47             if (type.IsValueType)   //target是值类型
 48             {
 49                 il.Emit(OpCodes.Ldloca_S, (byte)1);  //加载loc1的地址
 50                 il.Emit(OpCodes.Initobj, type);   //初始化loc1, loc1 = 0
 51             }
 52             else   //target是引用类型
 53             {
 54                 var types = new Type[length];
 55                 for (int i = startBound; i < startBound + length; i++)
 56                 {
 57                     //获取读到的db值的类型
 58                     types[i - startBound] = reader.GetFieldType(i);
 59                 }
 60                 //查找标记了ExplicitConstructor属性(Attribute)的构造函数
 61                 var explicitConstr = typeMap.FindExplicitConstructor();
 62                 if (explicitConstr != null)
 63                 {
 64                     #region 存在
 65                     var structLocals = new Dictionary<Type, LocalBuilder>();
 66 
 67                     var consPs = explicitConstr.GetParameters(); //获取该构造函数上的参数集
 68 
 69                     #region 遍历加载参数
 70                     foreach (var p in consPs)
 71                     {
 72                         //引用类型
 73                         if (!p.ParameterType.IsValueType)
 74                         {
 75                             //如果传入参数为复杂类型, 则以 null 来处理
 76                             il.Emit(OpCodes.Ldnull);
 77                         }
 78                         else    //值类型
 79                         {
 80                             LocalBuilder loc;
 81                             if (!structLocals.TryGetValue(p.ParameterType, out loc))
 82                             {
 83                                 //定义本地变量
 84                                 structLocals[p.ParameterType] = loc = il.DeclareLocal(p.ParameterType);
 85                             }
 86 
 87                             il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
 88                             il.Emit(OpCodes.Initobj, p.ParameterType);   //初始化传入参数, a=0,b=false之类的
 89                             il.Emit(OpCodes.Ldloca, (short)loc.LocalIndex);
 90                             il.Emit(OpCodes.Ldobj, p.ParameterType);   //加载初始化后的参数
 91                         }
 92                     }
 93                     #endregion
 94 
 95                     il.Emit(OpCodes.Newobj, explicitConstr);   //创建对象  new target(...);
 96                     il.Emit(OpCodes.Stloc_1);   //loc1 = target
 97 
 98                     //target 是否实现 ISupportInitialize 接口, 如果实现, 则调用其 BeginInit 方法
 99                     supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
100                     if (supportInitialize)
101                     {
102                         il.Emit(OpCodes.Ldloc_1);
103                         il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
104                     }
105                     #endregion
106                 }
107                 else
108                 {
109                     #region 不存在
110                     var ctor = typeMap.FindConstructor(names, types);  //查找构造函数, 优先返回无参构造函数
111                     if (ctor == null)
112                     {
113                         //找不到能用的构造函数
114                         string proposedTypes = "(" + string.Join(", ", types.Select((t, i) => t.FullName + " " + names[i]).ToArray()) + ")";
115                         throw new InvalidOperationException(string.Format("A parameterless default constructor or one matching signature {0} is required for {1} materialization", proposedTypes, type.FullName));
116                     }
117 
118                     if (ctor.GetParameters().Length == 0)
119                     {
120                         il.Emit(OpCodes.Newobj, ctor);
121                         il.Emit(OpCodes.Stloc_1);   //loc1 = new target();
122                         supportInitialize = typeof(ISupportInitialize).IsAssignableFrom(type);
123                         if (supportInitialize)
124                         {
125                             il.Emit(OpCodes.Ldloc_1);
126                             il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("BeginInit"), null);
127                         }
128                     }
129                     else
130                     {
131                         specializedConstructor = ctor;
132                     }
133                     #endregion
134                 }
135             }
136 
137             //try  开始
138             il.BeginExceptionBlock();   
139             if (type.IsValueType)
140             {
141                 //如果是值类型, 加载target的地址
142                 il.Emit(OpCodes.Ldloca_S, (byte)1);// [target]
143             }
144             else if (specializedConstructor == null)   //构造函数为无参构造函数
145             {
146                 //引用类型, 则直接使用变量即可
147                 il.Emit(OpCodes.Ldloc_1);// [target]
148             }
149 
150             //用reader中的列去匹配target中的属性, 匹配不上, 则显示为null, 此处的No为null
151             var members = (specializedConstructor != null
152                 ? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n))
153                 : names.Select(n => typeMap.GetMember(n))).ToList();  //无参
154 
155             // stack is now [target]
156 
157             bool first = true;
158             var allDone = il.DefineLabel();
159             int enumDeclareLocal = -1,
160                 //定义第二个本地变量,object类型的, 然后返回此本地变量的index值, 其实就是截止目前, 定义了本地变量的个数
161                 valueCopyLocal = il.DeclareLocal(typeof(object)).LocalIndex;
162             foreach (var item in members)
163             {
164                 if (item != null)
165                 {
166                     #region object to model
167 
168                     if (specializedConstructor == null)   //无参构造函数存在
169                         il.Emit(OpCodes.Dup); // stack is now [target][target]
170 
171                     Label isDbNullLabel = il.DefineLabel();
172                     Label finishLabel = il.DefineLabel();
173 
174                     il.Emit(OpCodes.Ldarg_0); // stack is now [target][target][reader]
175                     EmitInt32(il, index); // stack is now [target][target][reader][index]
176                     il.Emit(OpCodes.Dup);// stack is now [target][target][reader][index][index]
177                     il.Emit(OpCodes.Stloc_0);// stack is now [target][target][reader][index]     //loc0 = [index]
178                     //获取reader读取的值, reader[index]
179                     il.Emit(OpCodes.Callvirt, getItem); // stack is now [target][target][value-as-object]  
180                     il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object]
181                     StoreLocal(il, valueCopyLocal);  //将 reader[index]的值, 存放到本地变量 loc_valueCopyLocal 中
182 
183                     Type colType = reader.GetFieldType(index);   //reader[index] 的列的类型  source
184                     Type memberType = item.MemberType;   //target[item] 的类型  target
185 
186                     //如果目标类型为char 或者 char? , 则调用ReadChar / ReadNullableChar方法来完成转换
187                     if (memberType == typeof(char) || memberType == typeof(char?))
188                     {
189                         il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod(
190                             memberType == typeof(char) ? "ReadChar" : "ReadNullableChar", BindingFlags.Static | BindingFlags.Public), null); // stack is now [target][target][typed-value]
191                     }
192                     else
193                     {
194                         il.Emit(OpCodes.Dup); // stack is now [target][target][value-as-object][value-as-object]
195                         //判断是否为DBNull类型, 如果是, 则跳转到 标签isDbNullLabel
196                         il.Emit(OpCodes.Isinst, typeof(DBNull)); // stack is now [target][target][value-as-object][DBNull or null]
197                         il.Emit(OpCodes.Brtrue_S, isDbNullLabel); // stack is now [target][target][value-as-object]
198 
199                         // unbox nullable enums as the primitive, i.e. byte etc
200                         // int? -> int,   int/string -> null, 根据可空值类型来获取其值类型
201                         var nullUnderlyingType = Nullable.GetUnderlyingType(memberType);
202                         var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : memberType;
203 
204                         if (unboxType.IsEnum)
205                         {
206                             Type numericType = Enum.GetUnderlyingType(unboxType);
207                             if (colType == typeof(string))
208                             {
209                                 if (enumDeclareLocal == -1)
210                                 {
211                                     enumDeclareLocal = il.DeclareLocal(typeof(string)).LocalIndex;
212                                 }
213                                 il.Emit(OpCodes.Castclass, typeof(string)); // stack is now [target][target][string]
214                                 StoreLocal(il, enumDeclareLocal); // stack is now [target][target]
215                                 il.Emit(OpCodes.Ldtoken, unboxType); // stack is now [target][target][enum-type-token]
216                                 il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null);// stack is now [target][target][enum-type]
217                                 LoadLocal(il, enumDeclareLocal); // stack is now [target][target][enum-type][string]
218                                 il.Emit(OpCodes.Ldc_I4_1); // stack is now [target][target][enum-type][string][true]
219                                 il.EmitCall(OpCodes.Call, enumParse, null); // stack is now [target][target][enum-as-object]
220                                 il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
221                             }
222                             else
223                             {
224                                 FlexibleConvertBoxedFromHeadOfStack(il, colType, unboxType, numericType);
225                             }
226 
227                             if (nullUnderlyingType != null)
228                             {
229                                 il.Emit(OpCodes.Newobj, memberType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
230                             }
231                         }
232                         else if (memberType.FullName == LinqBinary)
233                         {
234                             il.Emit(OpCodes.Unbox_Any, typeof(byte[])); // stack is now [target][target][byte-array]
235                             il.Emit(OpCodes.Newobj, memberType.GetConstructor(new Type[] { typeof(byte[]) }));// stack is now [target][target][binary]
236                         }
237                         else
238                         {
239                             TypeCode dataTypeCode = Type.GetTypeCode(colType), 
240                                 unboxTypeCode = Type.GetTypeCode(unboxType);
241                             bool hasTypeHandler;
242                             if ((hasTypeHandler = typeHandlers.ContainsKey(unboxType)) || colType == unboxType || dataTypeCode == unboxTypeCode || dataTypeCode == Type.GetTypeCode(nullUnderlyingType))
243                             {
244                                 //判断是否有自定义的转换方法, 如果有, 则调用自定义的方法完成转换
245                                 if (hasTypeHandler)
246                                 {
247 #pragma warning disable 618
248                                     il.EmitCall(OpCodes.Call, typeof(TypeHandlerCache<>).MakeGenericType(unboxType).GetMethod("Parse"), null); // stack is now [target][target][typed-value]
249 #pragma warning restore 618
250                                 }
251                                 else
252                                 {
253                                     //将指令中指定类型的已装箱的表示形式转换成未装箱形式
254                                     il.Emit(OpCodes.Unbox_Any, unboxType); // stack is now [target][target][typed-value]
255                                 }
256                             }
257                             else
258                             {
259                                 // not a direct match; need to tweak the unbox
260                                 FlexibleConvertBoxedFromHeadOfStack(il, colType, nullUnderlyingType ?? unboxType, null);
261                                 if (nullUnderlyingType != null)
262                                 {
263                                     il.Emit(OpCodes.Newobj, unboxType.GetConstructor(new[] { nullUnderlyingType })); // stack is now [target][target][typed-value]
264                                 }
265                             }
266                         }
267                     }
268                     if (specializedConstructor == null)
269                     {
270                         // Store the value in the property/field
271                         if (item.Property != null)
272                         {
273                             if (type.IsValueType)
274                             {
275                                 il.Emit(OpCodes.Call, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
276                             }
277                             else
278                             {
279                                 il.Emit(OpCodes.Callvirt, DefaultTypeMap.GetPropertySetter(item.Property, type)); // stack is now [target]
280                             }
281                         }
282                         else
283                         {
284                             il.Emit(OpCodes.Stfld, item.Field); // stack is now [target]
285                         }
286                     }
287 
288                     il.Emit(OpCodes.Br_S, finishLabel); // stack is now [target]
289 
290                     il.MarkLabel(isDbNullLabel); // incoming stack: [target][target][value]
291                     if (specializedConstructor != null)
292                     {
293                         il.Emit(OpCodes.Pop);
294                         if (item.MemberType.IsValueType)
295                         {
296                             int localIndex = il.DeclareLocal(item.MemberType).LocalIndex;
297                             LoadLocalAddress(il, localIndex);
298                             il.Emit(OpCodes.Initobj, item.MemberType);
299                             LoadLocal(il, localIndex);
300                         }
301                         else
302                         {
303                             il.Emit(OpCodes.Ldnull);
304                         }
305                     }
306                     else
307                     {
308                         il.Emit(OpCodes.Pop); // stack is now [target][target]
309                         il.Emit(OpCodes.Pop); // stack is now [target]
310                     }
311 
312                     if (first && returnNullIfFirstMissing)
313                     {
314                         il.Emit(OpCodes.Pop);
315                         il.Emit(OpCodes.Ldnull); // stack is now [null]
316                         il.Emit(OpCodes.Stloc_1);
317                         il.Emit(OpCodes.Br, allDone);
318                     }
319 
320                     il.MarkLabel(finishLabel);
321                     #endregion
322                 }
323 
324                 first = false;
325                 index += 1;
326             }
327             if (type.IsValueType)
328             {
329                 il.Emit(OpCodes.Pop);
330             }
331             else
332             {
333                 //构造函数为有参的构造函数
334                 if (specializedConstructor != null)
335                 {
336                     //创建对象
337                     il.Emit(OpCodes.Newobj, specializedConstructor);
338                 }
339                 il.Emit(OpCodes.Stloc_1); // stack is empty
340 
341                 //实现 ISupportInitialize 接口, 调用 EndInit 方法, 完成初始化
342                 if (supportInitialize)
343                 {
344                     il.Emit(OpCodes.Ldloc_1);
345                     il.EmitCall(OpCodes.Callvirt, typeof(ISupportInitialize).GetMethod("EndInit"), null);
346                 }
347             }
348             il.MarkLabel(allDone);
349             //try 结束 -> catch 开始
350             il.BeginCatchBlock(typeof(Exception)); // stack is Exception
351             il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
352             il.Emit(OpCodes.Ldarg_0); // stack is Exception, index, reader
353             LoadLocal(il, valueCopyLocal); // stack is Exception, index, reader, value
354             il.EmitCall(OpCodes.Call, typeof(SqlMapper).GetMethod("ThrowDataException"), null);  //抛出异常
355             il.EndExceptionBlock();
356             //catch 结束
357 
358             il.Emit(OpCodes.Ldloc_1); // stack is [rval]   此处就是转换后的最终结果
359             if (type.IsValueType)
360             {
361                 il.Emit(OpCodes.Box, type);
362             }
363             il.Emit(OpCodes.Ret);
364 
365             return (Func<IDataReader, object>)dm.CreateDelegate(typeof(Func<IDataReader, object>));
366         }

其中的value-as-object是从reader中读取出来的未转换的数据, typed-value是转换后的数据

本想做成可收缩的, 但是那种展开后, 不能点, 只要一点, 就自动收起来了, 感觉不方便, 所以还是贴出来了

其中还有些地方不够详细, 不过对照着这个, 去看Dapper源码, 是可以看的懂了

 在下一篇中, 我会画出堆栈中的变化, 来减少理解难度

 

posted @ 2016-11-08 17:11  Sniper_ZL  阅读(7404)  评论(0编辑  收藏  举报