程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Emit學習(4),emit學習

Emit學習(4),emit學習

編輯:關於.NET

Emit學習(4),emit學習


感覺好久沒有寫博客了, 這幾天有點小忙, 接下來會更忙, 索性就先寫一篇吧. 後面估計會有更長的一段時間不會更新博客了.

廢話不多說, 先上菜.

一、示例

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源碼, 是可以看的懂了

 在下一篇中, 我會畫出堆棧中的變化, 來減少理解難度

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved