開源一個跨平臺運行的服務插件 - TaskCore.MainForm

来源:http://www.cnblogs.com/wangrudong003/archive/2016/10/13/5952388.html
-Advertisement-
Play Games

本次將要很大家分享的是一個跨平臺運行的服務插件 - TaskCore.MainForm,此框架是使用.netcore來寫的,現在netcore已經支持很多系統平臺運行了,所以將以前的Task.MainForm改良成跨平臺的服務共大家使用和相互交流;本來這篇應該分享的是nginx+iis+redis+ ...


本次將要很大家分享的是一個跨平臺運行的服務插件 - TaskCore.MainForm,此框架是使用.netcore來寫的,現在netcore已經支持很多系統平臺運行了,所以將以前的Task.MainForm改良成跨平臺的服務共大家使用和相互交流;本來這篇應該分享的是nginx+iis+redis+Task.MainForm構建分散式架構後續文章,但使用.netcore來定義一個服務插件和跨平臺測試經過讓我既興奮又頭痛,不說了下次再分享分散式後續文章吧;那麼開始今天的分享內容,希望各位多多支持:

 

. 框架結構介紹及運行效果圖

. 如何生成nuget包和使用開源框架TaskCore.MainForm

. win7和ubuntu16.04運行TaskCore.MainForm的兩種方式和測試截圖(也可認為.netcore項目在win7和ubuntu系統運行的兩種方式)

. 框架代碼解讀及感悟

 

下麵一步一個腳印的來分享:

. 框架結構介紹及運行效果圖

首先,咋們先來認識下項目源碼的工程目錄結構如圖:

結構看上去應該夠清晰了,源碼文件量也很少,不過的確實現了動態載入程式集dll來執行任務,後面會給出相應的執行截圖,我們再來看下TaskCore.MainForm項目通過vs2015生成之後Bin文件夾中的文件如圖:

如果安裝了.netcore sdk的話在windows上您只需要上面截圖的文件就能運行插件了;再來我們在已經安裝過core sdk的win7系統上執行一下如下命令 dotnet TaskCore.MainForm.dll 就能看到如圖的效果:

 沒錯這就是插件運行起來的效果圖,因為.netcore現目前暫時沒有提供類似於winform那樣的皮膚界面效果,所有隻能通過命令行來做跨平臺運行程式

 

. 如何生成nuget包和使用TaskCore.MainForm

首先,我們要明確服務是由兩部分構成(TaskCore.MainFormTaskCore.Plugin);TaskCore.MainForm主要用來運行程式,TaskCore.Plugin用來作為子任務需要繼承的父類插件;當我們下載TaskCore.MainForm運行包之後(如圖2結構),我們還需要做的就是繼承TaskCore.Plugin.dl中的TPlugin這個類,來重寫自己的任務內容,因此我們新建一個項目取名為:TaskCore.Test,再來我們通過vs2015引用功能直接添加TaskCore.MainForm運行包中的TaskCore.Plugin.dl引用,這個時候會提示一個錯誤:

錯誤意思是沒法載入netcore版本的dll,因此這種直接在vs項目中引用方式添加dll依賴不行,需要通過nuget來添加依賴包(.netcore對類庫的引用目前只能通過nuget來安裝依賴,這個需要註意),所以我這裡把TaskCore.Plugin項目通過 dotnet pack 命令來生成nuget包,然後以便我TaskCore.Test項目中來使用;

如何生成nuget包(win7系統dotnet命令來生成包的過程和下載):

直接在vs中右鍵您需要打包的類庫項目-》選擇"在資源管理器中打開文件夾"-》這樣就到了您類庫的根目錄,然後退到類庫根目錄的上一級文件夾中-》按住鍵盤”shift"鍵並同時滑鼠右鍵要打包類庫的項目文件件(我這裡是TaskCore.Plugin文件夾)-》選擇“在此處打開命令窗體” 這個時候就進入了cmd命令窗體,當然有些朋友直接喜歡直接先cmd再來找對應的磁碟,反正我覺得第一種更快點(不同人不同選擇吧),下麵看下操作截圖:

由上圖可以看到,通過命令生成了nupkg文件,這文件就是咋們需要在項目中下載安裝的TaskCore.Plugin插件包;接下來我們來在TaskCore.Test項目中安裝這個插件如下步奏,右鍵TaskCore.Plugin項目中的“引用”-》選擇“管理nuget程式包”-》然後選擇右上角的這個圖標

-》選擇“Nuget包管理器”-》“程式包源”-》

-》然後選擇新創建的包源,下麵設置如圖

這裡的“源(S)”指定的本地路徑就是剛纔我們生成的nupkg文件文件的磁碟(當然我這裡是吧剛纔pack命令生成的文件複製到了MyNugPackage文件夾中方便測試)-》然後點擊”確定“按鈕-》然後在返回到

選擇我們的包源MyNugPackage-》再瀏覽這裡就能看到我們創建的nuget包了

-》選擇並安裝-》好安裝好後我們可以查看“引用”下麵有瞭如下變化

並且project.json文件中自動添加了依賴項:

好了到我們的TaskCore.Test項目中就可以使用Task.Plugin包裡面的方法很類等信息了;

使用TaskCore.MainForm

由上面的操作TaskCore.Test項目已經安裝了Task.Plugin包,那麼我們在項目中分別創建3個子類並繼承自Task.Plugin包的父類TPlugin並重寫方法TPlugin_Load(),對應文件名稱分別為:BlogsObj.cs,BlogsObj01.cs,BlogsObj02.cs,分別添加入下代碼內容:

BlogsObj.cs:

 1 namespace TaskCore.Test
 2 {
 3     // This project can output the Class library as a NuGet Package.
 4     // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
 5     public class BlogsObj : TPlugin
 6     {
 7         public BlogsObj()
 8         {
 9 
10         }
11 
12         public override void TPlugin_Load()
13         {
14             var sbLog = new StringBuilder(string.Empty);
15             try
16             {
17                 sbLog.Append($"這裡是BlogsObj,獲取配置文件:{this.XmlConfig.Name}");
18 
19                 //代碼塊
20                 //
21 
22                 new WriteLog()._WriteLog($"{DateTime.Now}測試引用nuget包");
23 
24             }
25             catch (Exception ex)
26             {
27                 sbLog.Append($"異常信息:{ex.Message}");
28             }
29             finally
30             {
31 
32                 PublicClass._WriteLog(sbLog.ToString(), this.XmlConfig.Name);
33             }
34         }
35     }
36 }

BlogsObj01.cs:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using TaskCore.Plugin;
 7 
 8 namespace TaskCore.Test
 9 {
10     // This project can output the Class library as a NuGet Package.
11     // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
12     public class BlogsObj01 : TPlugin
13     {
14         public BlogsObj01()
15         {
16 
17         }
18 
19         public override void TPlugin_Load()
20         {
21             var sbLog = new StringBuilder(string.Empty);
22             try
23             {
24                 sbLog.Append($"這裡是BlogsObj01,獲取配置文件:{this.XmlConfig.Name}");
25 
26                 //代碼塊
27                 //
28             }
29             catch (Exception ex)
30             {
31                 sbLog.Append($"異常信息:{ex.Message}");
32             }
33             finally
34             {
35                 //Console.WriteLine($"這裡是Blogs,獲取配置文件:{this.XmlConfig.Name}");
36                 PublicClass._WriteLog(sbLog.ToString(), this.XmlConfig.Name);
37             }
38         }
39     }
40 }
View Code

BlogsObj02.cs:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Threading.Tasks;
 5 using TaskCore.Plugin;
 6 
 7 namespace TaskCore.Test
 8 {
 9     // This project can output the Class library as a NuGet Package.
10     // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build".
11     public class BlogsObj02 : TPlugin
12     {
13         public BlogsObj02()
14         {
15 
16         }
17 
18         public override void TPlugin_Load()
19         {
20             //Console.WriteLine($"這裡是Blogs,獲取配置文件:{this.XmlConfig.Name}");
21             PublicClass._WriteLog($"這裡是BlogsObj02,獲取配置文件:{this.XmlConfig.Name}", this.XmlConfig.Name);
22         }
23     }
24 }
View Code

好了測試代碼寫完,咋們生成一下,然後把這個TaskCore.Test.dll拷貝到TaskCore.MainForm運行包中,還需要在PluginXml文件夾中分別創建繼承TPlugin的子類的配置文件對應的截圖如(註:這裡xml配置文件名稱必須和子類名稱一樣):

配置文件內容如BlogsObj.xml

 1 <!--
 2   1.xml配置模板
 3   2.utf-8文件
 4   3.複製到程式根目錄下麵PluginXml文件夾下
 5   4.每個任務建議創建和程式dll名稱相同xml配置文件
 6 -->
 7 <TaskMain>
 8   <!--固定:執行任務時間計時器(分鐘)-->
 9   <Timer>1</Timer>
10   <!--固定:任務名稱-->
11   <Name>獲取博客信息</Name>
12   <!--固定:賬號-->
13   <UserName></UserName>
14   <!--固定:密碼-->
15   <UserPwd></UserPwd>
16   <!--固定:key-->
17   <ApiKey></ApiKey>
18   <!--固定:key-->
19   <ApiUrl></ApiUrl>
20   <!--固定:是否關閉任務 1:是 0:否-->
21   <CloseTask>0</CloseTask>
22   
23 <!--固定:描述-->
24   <Des>獲取博客信息</Des>
25   <!--自定義:其他配置信息-->
26   <Other>
27     <ShenNiuBuXing3>神牛步行3</ShenNiuBuXing3>
28   </Other>
29 </TaskMain>

最後在CrossFiles.xml配置文件中添加TaskCore.Test.dll文件名稱如:

1 <!--
2   CrossFiles指定對應任務的dll文件,必須存在的文件
3 -->
4 <TaskMain>
5   <File>TaskCore.Test.dll</File>
6 </TaskMain>

好了完成了,我們在windows開發環境上運行看下效果圖:

到這裡我們來簡單總結下怎麼使用TaskCore.MainForm插件,在自己項目中安裝TaskCore.Plugin的nuget包-》重寫父類TPlugin的TPlugin_Load()方法-》生成自己的項目,拷貝項目的dll到TaskCore.MainForm運行包中-》在運行包中PluginXml文件夾中增加任務子類相同類名的xml文件並配置如上的配置信息-》增加CrossFiles.xml中的任務dll文件配置-》使用命令 dotnet TaskCore.MainForm.dll 運行服務插件,挺簡單的吧

 

. win7和ubuntu16.04運行TaskCore.MainForm的兩種方式和測試截圖(也可認為.netcore項目在win7和ubuntu系統運行的兩種方式)

 由於環境影響,這裡跨平臺運行測試我只測試win7ubuntu16.04,其他系統的發佈測試希望朋友們在使用過程中得到結果告知我下謝謝;先來講在win7中運行

1. 安裝了netcore sdk環境的發佈運行

安裝了sdk後直接可以在cmd命令中 dotnet TaskCore.MainForm.dll 來運行服務,開篇上面講解的示例都是在安裝了sdk後的截圖,服務運行所需要的文件如圖:

就只有這些文件(當然程式需要的某些平臺依賴項使用的是安裝的sdk中的文件,所以看起來很少實際應該包含netcore sdk裡面的文件才能運行),通過命令運行的效果圖:

 

2. 未安裝netcore sdk環境的發佈運行

在為安裝sdk平臺上系統上運行才是重點,這樣才可以說是跨平臺;首先,為了更好看效果我們複製一份如圖的兩個文件到TaskCore.MainForm01文件夾中:

沒錯,只需要這兩個文件,然後我們需要修改project.json文件的內容如下註釋的地方:

 1 {
 2   "version": "1.0.0-*",
 3   "buildOptions": {
 4     "emitEntryPoint": true
 5   },
 6 
 7   "dependencies": {
 8     "Microsoft.NETCore.App": {
 9       //"type": "platform",    跨平臺發佈需要註釋
10       "version": "1.0.0"
11     },
12     "System.IO.FileSystem": "4.0.1",
13     "System.Reflection": "4.1.0",
14     "System.Text.Encoding.CodePages": "4.0.1",
15     "System.Threading.Timer": "4.0.1",
16     "System.Xml.XDocument": "4.0.11",
17     "TaskCore.Plugin": "1.0.0"
18   },
19 
20   "frameworks": {
21     "netcoreapp1.0": {
22       "imports": "dnxcore50"
23     }
24   },
25   //跨平臺發佈需要添加如下節點
26   "runtimes": {
27     "ubuntu.16.04-x64": { }, //運行在ubuntu.16.04的64位系統
28     "win7-x64": { }    //運行在win7的64位系統
29   }
30 }

然後使用cmd分別錄入並回車運行指令 dotnet restore 此時TaskCore.MainForm01文件夾中會自動生成一個project.lock.json文件(大家註意看下),然後再錄入指令 dotnet publish -r win7-x64 ,可以看到命令窗信息如:

就表示成功了,並且有返回生成運行文件的路徑,我們按照路徑找到生成的文件publish文件夾,裡面沒有PluginXml配置文件夾和配置文件和測試項目TaskCore.Test.dll,我們為了方便直接複製上面配置好的配置文件到publish文件夾中如下截圖就是生成的發佈文件:

沒錯TaskCore.MainForm01.exe這個就是自動生成的運行文件,然後我們雙擊運行效果圖:

 

運行成功了,有朋友會問您之前系統不是安裝了sdk麽,這種測試能算麽,我想說的是這個我通過QQ發給qq群裡面的朋友@南宮亦琦(不要怪我貼出了您的昵稱)測試過了,她沒有安裝過sdk的;

ubuntu16.04運行:

1. 安裝了netcore sdk環境的發佈運行

首先,我們需要把在win7上生成的項目複製到ubuntu系統磁碟中(我們使用上面配置好的TaskCore.MainForm包),我這裡採用共用目錄的方式把文件拷貝到ubuntu系統磁碟上,如圖:

然後,滑鼠右鍵點擊空白地方,選擇“在終端打開”,然後在窗體中錄入如下指令 dotnet TaskCore.MainForm.dll ,能看到如下運行起的結果:

這個命令方式是不是很上面win7上的方式很相同,本來就是一樣的,只要安裝了.netcore sdk這種方式幾乎可以共用

2. 未安裝netcore sdk環境的發佈運行

 不安裝sdk環境運行才是咋們需要關註的,下麵就來看下怎麼生成再ubuntu系統上能運行的文件;我們複製一份上面的TaskCore.MainForm01項目到TaskCore.MainForm02中來測試,由於之前我們在講生成win7執行文件時候,執行了命令 dotnet restore 和 dotnet publish -r win7-x64 命令所以文件中有project.lock和bin文件夾,為了測試我們需要刪除掉部分文件,只剩下如圖文件,這裡需要註意的是之前已經配置過project.xml我們無需在修改了:

然後,分別執行和生成win7運行文件幾乎相同的命令 dotnet restore 和 dotnet publish -r ubuntu.16.04-x64 不同之處在於生成的文件存放的位置不同和使用的運行環境不同,運行結果如圖:

publish文件夾中的文件就是在ubuntu系統執行的文件,然後我們需要把任務配置文件夾PluginXml測試項目TaskCore.Test.dll複製到該目錄中,再來咋們通過共用吧TaskCore.MainForm02目錄中的publish複製到ubuntu中;然後我們需要把TaskCore.MainForm02可執行文件設置下許可權,滑鼠右鍵點擊TaskCore.MainForm02可執行文件,選擇“屬性”,再選擇“許可權”選項卡,勾選“允許作為程式執行”,如圖:

然後,我們右鍵文件夾中空白處,選擇“在終端打開”,再執行如下命令 ./TaskCore.MainForm02 ,最後看運行效果圖:

好了,發佈在ubuntu系統上執行文件和運行的步奏就這些,幾乎和win7上相同

 

. 框架代碼解讀及感悟

怎麼使用跨平臺TaskCore.MainForm任務框架講解完了,下麵來具體看下主要代碼Program.cs文件中:

  1 namespace TaskCore.MainForm
  2 {
  3     /// <summary>
  4     /// author 神牛步行3
  5     /// contact [email protected]
  6     /// des TaskCore.MainForm跨平臺插件由神牛步行3提供
  7     /// </summary>
  8     public class Program
  9     {
 10 
 11         private static Dictionary<string, MoAssembly> dicTasks = new Dictionary<string, MoAssembly>();
 12 
 13         public static void Main(string[] args)
 14         {
 15             //註冊編碼,防止亂碼
 16             Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
 17 
 18             //初始化程式集文件
 19             _Init();
 20 
 21             //是否繼續開啟任務,預設沒有待執行任務,不提示
 22             if (dicTasks.Count <= 0) { _LoopAlert("是否退出?(Y/N)"); return; }
 23             _LoopAlert("是否開始執行任務?(Y / N)");
 24 
 25             //執行任務
 26             foreach (var item in dicTasks.Values)
 27             {
 28                 //使用Task防止異常後相互影響
 29                 Task.Run(() =>
 30                 {
 31                     try
 32                     {
 33 
 34                         //創建任務對象
 35                         var tp = item.Asm.CreateInstance(item.FullName) as TPlugin;
 36                         if (!string.IsNullOrEmpty(tp.XmlConfig.TpError)) { _Alert($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{tp.XmlConfig.Name} - 異常信息:{tp.XmlConfig.TpError}"); }
 37                         else
 38                         {
 39 
 40                             //timer定時器
 41                             var timer = new Timer((param) =>
 42                             {
 43                                 var msg = $"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{tp.XmlConfig.Name}";
 44                                 try
 45                                 {
 46                                     var tpObj = param as TPlugin;
 47                                     //是否關閉暫停任務
 48                                     if (tpObj.XmlConfig.CloseTask) { return; }
 49                                     _Alert($"{msg} - 開始執行...{tp.XmlConfig.Timer}分鐘一次");
 50                                     //任務入口
 51                                     tpObj.TPlugin_Load();
 52                                 }
 53                                 catch (Exception ex) { _Alert($"{msg} - 異常信息:{ex.Message}"); }
 54                             }, tp, 0, 1000 * 60 * tp.XmlConfig.Timer);
 55                         }
 56 
 57                     }
 58                     catch (Exception ex)
 59                     {
 60                         _Alert($"{DateTime.Now.ToString("yyyy/MM/dd HH:mm")}:{item.Name} - 異常信息:{ex.Message}");
 61                     }
 62                 });
 63             }
 64             _LoopAlert("正在監控執行的任務,是否退出?(Y / N)");
 65         }
 66 
 67         /// <summary>
 68         /// 初始化程式集文件
 69         /// </summary>
 70         private static void _Init()
 71         {
 72             try
 73             {
 74 
 75                 _Alert("初始化任務中...");
 76                 //獲取文件
 77                 var files = PublicClass._GetPluginFile("");
 78                 if (files.Length <= 0) { _Alert("未能找到可用的程式集,請檢查配置"); return; }
 79 
 80                 //讀取任務文件
 81                 _Alert("讀取CrossFiles.xml配置中...");
 82                 var baseAddr = Path.Combine(Directory.GetCurrentDirectory(), "PluginXml", "CrossFiles.xml");
 83                 var doc = XDocument.Load(baseAddr);
 84                 var fileables = files.AsEnumerable();
 85                 var taskFiles = new List<FileInfo>();
 86                 foreach (var item in doc.Root.Nodes())
 87                 {
 88                     var crossFile = item.ToString().ToUpper();
 89                     var choiceFiles = fileables.Where(b => crossFile.Contains(b.Name.ToUpper()));
 90                     if (!choiceFiles.Any()) { continue; }
 91 
 92                     taskFiles.AddRange(choiceFiles);
 93                 }
 94 
 95                 //展示文件信息
 96                 _Alert($"待遍歷{taskFiles.Count}個文件信息...");
 97                 foreach (var item in taskFiles.OrderBy(b => b.CreationTime))
 98                 {
 99                     var asmName = new AssemblyName($"{item.Name.Replace(".dll", "")}");
100                     Assembly sm = Assembly.Load(asmName);
101                     if (sm == null) { continue; }
102                     var ts = sm.GetTypes();
103                     //判斷特定的任務類,加入任務dic
104                     foreach (var t in ts.Where(b => b.Name != "TPlugin" && b.GetMethod("TPlugin_Load") != null))
105                     {
106 
107                         dicTasks.Add(
108                             t.FullName,
109                             new MoAssembly
110                             {
111                                 Asm = sm,
112                                 FullName = t.FullName,
113                                 Name = t.Name
114                             });
115                     }
116                 }
117                 _Alert($"獲取待執行任務量:{dicTasks.Count}個");
118             }
119             catch (Exception ex)
120             {
121                 _Alert($"異常信息:{ ex.Message}");
122             }
123         }
124 
125         /// <summary>
126         /// 消息提醒
127         /// </summary>
128         /// <param name="msg">提示信息</param>
129         /// <param name="isReadLine">是否需要用戶輸入指令</param>
130         /// <returns>用戶錄入的指令</returns>
131         private static string _Alert(string msg = "神牛步行3-消息提醒", bool isReadLine = false)
132         {
133             Console.WriteLine(msg);
134             if (isReadLine) { return Console.ReadLine(); }
135             return "";
136         }
137 
138         private static void _LoopAlert(string msg = "是否開始執行任務?(Y/N)")
139         {
140             do
141             {
142                 var readKey = _Alert(msg, true);
143                 if (readKey.ToUpper().Contains("Y")) { break; }
144
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近寫代碼,遇到一個問題,微軟基於List<T>自帶的方法是public bool Remove(T item);,可是有時候我們可能會用到諸如RemoveAll<IEnumerable<T>>的方法,坦白的說,就是傳入的參數是一個IEnumerable<T>,而不是一個T,這種情景是隨時可能用到的 ...
  • 使用C#處理基於比特流的數據 0x00 起因 最近需要處理一些基於比特流的數據,電腦處理數據一般都是以byte(8bit)為單位的,使用BinaryReader讀取的數據也是如此,即使讀取bool型也是一個byte。不過藉助於C#基礎類庫中提供的一些方法,也實現了對基於比特的數據的讀取。任務完成後 ...
  • 初識MVC-controller隨筆 之前用的一些其他框架,也沒有系統性的學習MVC框架。最近才開始接觸,給大家簡單的分享一下經驗。 1 MVC的核心就是Controller(控制器),它負責處理瀏覽器傳送過來的所有請求,並決定要將什麼內容響應給瀏覽器。但Controller並不負責決定內容應該如何 ...
  • 調用百度api微博熱門精選介面,使用了volley,簡單說說volley get的請求方式的使用 header的設置和請求參數的設置,見代碼如下: ...
  • 目錄 1. ApiController 2. HttpActionDescriptor 3. IHttpActionSelector ApiController 在上節中,講到如何選擇並激活對應的IHttpController,而一般我們在開發中使用的是ApiController 在ApiContr ...
  • 編寫MFC程式時,想列印出調試信息,使用cout後,發現程式並沒有像想象中那樣自動彈出命令行視窗,要輸出的信息也沒地方去查看。百度後知道要手動調出命令行視窗,才可以看到輸出的信息。 百度上介紹了兩種方法,一種是通過添加代碼,在程式中建立命令行視窗的對象。這裡介紹一種比較簡單的方法。 右鍵解決方案,打 ...
  • 1、視圖中 2、控制器的action中 3、過濾器中 比如在ActionFilterAttribute中,這個時候一般是自己實現一個繼承類,然後重寫相關的方法。 在重寫的方法中如果需要控制器的名稱。 4、公共方法中 ...
  • (本文是從我的舊博客遷移過來的) 問題地址:http://acm.timus.ru/problem.aspx?space=1&num=1258 前幾日在博客園看到這種線上測試的時候,有一種相見恨晚的感覺,於是隨便選了一道感興趣的題(No.1258:Pool)開始做。為了準確瞭解題的意思,我把題翻譯成 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...