Emit學習(4) - Dapper解析之數據對象映射(一)

来源:http://www.cnblogs.com/elvinle/archive/2016/11/08/6043701.html
-Advertisement-
Play Games

感覺好久沒有寫博客了, 這幾天有點小忙, 接下來會更忙, 索性就先寫一篇吧. 後面估計會有更長的一段時間不會更新博客了. 廢話不多說, 先上菜. 一、示例 1. 先建類, 類的名稱與讀取的表名並沒有什麼關係,可以不一樣, 然後就是其中的屬性大小寫不限 2. 測試代碼 接下來, 可以進入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); <

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題 怎麼樣將 Asp.Net Web Api 加入到 Asp.Net Web From 應用程式中 解決方案 在 Visual Studio 2013 中,創建新的 Web From,可以直接在"新建 ASP.NET 項目" 創建項目嚮導中,勾選ASP.NET Web API ,將其加入進來。如圖 ...
  • 近期接手了一個水電系統。感覺個人開發的心理還是不夠成熟,打算記錄一下開發的時候自己的心理供反省。 前情回顧。 11.6 開始真正的開發,依據之前建立的demo頁面。簡單的做了資料庫的設計,依據工具和MVC便捷的特性飛速完成了幾個表簡單的CRD。耗時1.5H。感覺還不錯。 11.7 繼續之前的幾個表的 ...
  • TchApp 為dotnet core配個UI,項目已托管github https://github.com/tnelab/tchapp 項目組需要里的幫助! ...
  • 客戶需求: 上兩篇博客主要介紹了通過Word進行費用報銷憑證的列印及批量列印。 雖然可以進行列印,但是還是存在問題,有的客戶機器MS Office 安裝會有問題,那麼通過DocX產生的Word在客戶機器上無法打開,所以還是有些瑕疵。 還有一個問題,就是這樣的列印體驗很不好,因為每次列印要把word下 ...
  • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Runtime.Serialization.Formatters.B ...
  • 2016-11-08 劉鐵錳大師MVVM視屏教程及代碼:www.cnblogs.com/prism/ 詳細文件下載地址: http://down.51cto.com/data/306663 絕對贊 ...
  • define symbol undef symbol if symbol [operator symbol2]... else elif symbol [operator symbol2] endif warning text text指在編譯器輸出中的警告文字 error text text指在編 ...
  • 新開博客,程式人生的一個新起點,創個博客紀念一下 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...