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源码, 是可以看的懂了
在下一篇中, 我会画出堆栈中的变化, 来减少理解难度