.Net Core應用框架Util介紹(三)

来源:https://www.cnblogs.com/xiadao521/archive/2018/09/17/Util-Introduction-3.html
-Advertisement-
Play Games

上篇介紹了Util的開發環境,並讓你把Demo運行起來。本文將介紹該Demo的前端Angular運行機制以及目錄結構。 目錄結構 在VS上打開Util Demo,會看見如下的目錄結構。 現代前端通常採用VS Code開發,不過我們為了使用TagHelper,需要採用VS開發,這為你提供了更多的選擇。 ...


  上篇介紹了Util的開發環境,並讓你把Demo運行起來。本文將介紹該Demo的前端Angular運行機制以及目錄結構。

 

目錄結構

    在VS上打開Util Demo,會看見如下的目錄結構。

  

  現代前端通常採用VS Code開發,不過我們為了使用TagHelper,需要採用VS開發,這為你提供了更多的選擇。 

  你可以將WebApi和Angular應用放在同一個項目中,就像現在看見的那樣。也可以分別把WebApi和Angular應用放到不同項目中。 

  如果你已經習慣了VS Code開發,這同樣沒問題,不過你將放棄TagHelper帶來的強類型代碼提示和編譯時檢查特性。 

  對於Angular,它提供了ng cli命令行工具,你可以用ng cli來創建項目結構。

  前文已簡要介紹了TagHelper,它是用來提升Angular視圖頁面開發效率的利器。為了使用TagHelper,不得不放棄ng cli,因為它不支持在Angular組件上配置服務端動態地址。

  下麵介紹這個項目中包含的目錄和文件。

 

Apis目錄

  這個目錄用來存放Web Api控制器。

  ApplicationController演示了普通CRUD操作,RoleController演示了樹型層次的CRUD操作。

  你暫時不要關心Web Api CRUD操作,我會在後續介紹。

 

 Areas目錄

   用過Asp.Net Mvc的同學可能知道,Areas就是區域,它的作用是提供模塊化管理。我們把不同的模塊用Areas的區域分隔開,這樣在項目規模變大時,還能迅速找到相關頁面。

  與傳統Asp.Net Mvc應用不同,Util的Areas控制器並不進行任何操作,只是簡單的返回視圖頁面,cshtml僅起到代碼生成器的作用。

  一個更好的選擇是使用RazorPage,它把控制器和頁面合併了,將來會使用這種方式。 

Configs目錄

   你並不需要它,我在Demo中用來放測試配置,項目上我通常把Configs目錄放在應用層類庫。

Controllers目錄

   Controllers目錄是用來放置與首頁相關的控制器。

Datas目錄  

  Util引入了DDD經典架構,Datas位於基礎設施層,一些人把它叫倉儲層。

  Datas通常放在單獨的類庫,為了演示簡單,我放在該WEB項目的目錄中。

DbScripts目錄

  這個目錄提供了Sql Server建庫腳本。

  一些人可能很驚訝,什麼年代了,還在使用Db First開發。

  在多年的開發實戰中,我摸索到一套以PowerDesigner數據建模配合CodeSmith代碼生成的開發模式。對於CRUD,它具有快速高效的特點,同時你還能擁有清晰的數據字典以供未來查閱。

  對於具備面向對象編程能力的人,這種方式並不會降低代碼質量和設計水平,在將代碼生成出來以後,通過手工調整就可達到與Code First相同的代碼水平。

  我會在未來某個合適的時候介紹這種開發模式。

 Domains目錄

  DDD經典架構中領域層相關的目錄,實際開發中將放到單獨的類庫。

Services目錄

  DDD經典架構中應用層相關的目錄,實際開發中將放到單獨的類庫。

Typings目錄

   Angular相關的所有東西都在這裡。

 

  app目錄用來存放與業務相關的項目資源,比如Angular組件,指令,服務等。

  值得註意的是,該目錄包含組件對應的.html文件,這些.html文件和.cshtml文件是怎樣的關係?

 

 

  如果你從未運行過Util Demo項目,打開app目錄,並未找到任何.html文件。

  你可能已經猜到了,.html文件是由.cshtml文件生成的

  你永遠都不應該手工編輯這些.html文件,因為在調試運行時將被覆蓋。 

  test目錄包含Ts單元測試,我僅對極少數Helper進行單元測試。通過下麵的npm命令把測試運行起來。

npm test

 

  util目錄包含對Angular常用API和Angular Material組件的封裝。

  

 

  Angular組件由視圖和控制器兩部分構成。視圖即模板頁,包含html標簽。控制器用來編寫邏輯,包含Ts代碼。換句話說,Angular應用開發主要是編寫html和ts(當然還有css,暫時不要管它)。

  TagHelper並不是Util封裝Angular的唯一手段,對於Angular控制器,Util採用鏈式封裝手法,將Angular常用Api封裝得更加簡單易用,使你對Angular Api只要有一個模糊的印象就可以開發了。

  對於Angular視圖頁面,並不能直接採用TagHelper簡單包裝,這樣會導致TagHelper過於複雜,另外很多功能需要在運行時進行判斷,TagHelper只在開發調試階段存在,所以採用兩層封裝會更加省力。

  首先採用Angular組件或指令對Material組件進行封裝,然後採用TagHelper提供強類型提示。  

  對於希望採用VS Code開發的同學,Typings/util目錄中封裝的代碼同樣可以使用,它跟TagHelper沒有什麼關係,你可以把它Copy到你的項目,我尚未把它發佈到npm。 

Views目錄

  Views目錄包含首頁。

appsettings.json文件

  它是一個配置文件,資料庫連接字元串在這裡。

nlog.config文件

  它是NLog日誌組件的配置文件,Util 採用NLog輸出開發調試和錯誤日誌,預設位置是c:\log目錄。

package.json文件

  它是npm包管理器的配置文件。

Program.cs文件

  它是Asp.Net Core程式入口點文件。

Startup.cs文件

  它是Asp.Net Core啟動文件,在這裡配置依賴註入和中間件請求管道。

tsconfig.json文件

  它是Typescript語言配置文件。

webpack.config.js文件

  它是Webpack自動化構建工具的配置文件。

  還有兩個配置文件隱藏在webpack.config.js下,它們對util和第三方Js框架進行處理。

 

運行機制

  對於沒有前端基礎的同學,可能很難理解這個Demo是如何運行起來的,下麵為你介紹這個Demo的運行機制,我們從npm包還原開始。 

 npm還原

  當你輸入yarncnpm install node-sass,它會找到package.json文件的dependencies節,然後把需要的文件下載到node_modules目錄中。

 執行Webpack構建

  然後輸入npm run dev,這裡發生了什麼?

  npm run是npm的一個命令,它會查找package.jsonscripts定義的命令。

 

  • npm run dev 

  dev就是npm run要查找的命令名,它是一個約定俗成的名稱,代表開發階段配置,即develop,當然你不一定用這個名字,叫abc也可以。

  npm run dev查找到package.json文件scripts節定義的dev命令,它的內容是npm run vendor && npm run app,這個命令是由兩個npm run命令組成的。

  •  npm run vendor

  npm run vendor的內容是webpack --config webpack.config.vendor.js,這將對webpack.config.vendor.js執行構建操作。

  webpack命令預設查找webpack.config.js文件,現在要查找的是webpack.config.vendor.js,所以需要添加參數—config。

  我們來看看webpack.config.vendor.js包含什麼內容。 

  1 const pathPlugin = require('path');
  2 const webpack = require('webpack');
  3 var Extract = require("extract-text-webpack-plugin");
  4 
  5 //第三方Js庫
  6 const jsModules = [
  7     'reflect-metadata',
  8     'zone.js',
  9     'moment',
 10     '@angular/animations',
 11     '@angular/common',
 12     '@angular/common/http',
 13     '@angular/compiler',
 14     '@angular/core',
 15     '@angular/forms',
 16     '@angular/elements',
 17     '@angular/platform-browser',
 18     '@angular/platform-browser/animations',
 19     '@angular/platform-browser-dynamic',
 20     '@angular/router',
 21     '@angular/cdk/esm5/collections.es5',
 22     '@angular/flex-layout',
 23     '@angular/material',
 24     'primeng/primeng',
 25     'lodash',
 26     "echarts-ng2"
 27 ];
 28 
 29 //第三方Css庫
 30 const cssModules = [
 31     '@angular/material/prebuilt-themes/indigo-pink.css',
 32     'material-design-icons/iconfont/material-icons.css',
 33     'font-awesome/css/font-awesome.css',
 34     'primeicons/primeicons.css',
 35     'primeng/resources/themes/omega/theme.css',
 36     'primeng/resources/primeng.min.css'
 37 ];
 38 
 39 module.exports = (env) => {
 40     //是否開發環境
 41     const isDev = !(env && env.prod);
 42     const mode = isDev ? "development" : "production";
 43 
 44     //將css提取到單獨文件中
 45     const extractCss = new Extract("vendor.css");
 46 
 47     //獲取路徑
 48     function getPath(path) {
 49         return pathPlugin.join(__dirname, path);
 50     }
 51 
 52     //打包第三方Js庫
 53     let vendorJs = {
 54         mode: mode,
 55         entry: { vendor: jsModules },
 56         output: {
 57             publicPath: 'dist/',
 58             path: getPath("wwwroot/dist"),
 59             filename: "[name].js",
 60             library: '[name]'
 61         },
 62         resolve: {
 63             extensions: ['.js']
 64         },
 65         devtool: "source-map",
 66         plugins: [
 67             new webpack.DllPlugin({
 68                 path: getPath("wwwroot/dist/[name]-manifest.json"),
 69                 name: "[name]"
 70             }),
 71             new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, getPath('./Typings')),
 72             new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, getPath('./Typings')),
 73             new webpack.IgnorePlugin(/^vertx$/)
 74         ]
 75     }
 76 
 77     //打包css
 78     let vendorCss = {
 79         mode: mode,
 80         entry: { vendor: cssModules },
 81         output: {
 82             publicPath: './',
 83             path: getPath("wwwroot/dist"),
 84             filename: "[name].css"
 85         },
 86         devtool: "source-map",
 87         module: {
 88             rules: [
 89                 { test: /\.css$/, use: extractCss.extract({ use: isDev ? 'css-loader' : 'css-loader?minimize' }) },
 90                 {
 91                     test: /\.(png|jpg|gif|woff|woff2|eot|ttf|svg)(\?|$)/, use: {
 92                         loader: 'url-loader',
 93                         options: {
 94                             limit: 20000,
 95                             name: "[name].[ext]",
 96                             outputPath: "images/"
 97                         }
 98                     }
 99                 }
100             ]
101         },
102         plugins: [
103             extractCss
104         ]
105     }
106     return isDev ? [ vendorJs, vendorCss] : [vendorCss];
107 }
webpack.config.vendor.js

  vendorJs 對象用於配置將哪些第三方Js框架文件進行打包,vendorCss 對象用於配置需要打包的第三方框架提供的Css文件。 

  entry屬性指定了需要打包的入口文件,output屬性則指定輸出的位置和文件名。 

  當webpack.config.vendor.js執行完畢,會在Util.Samples.Webs項目的wwwroot目錄創建一個dist子目錄,並生成vendor.jsvendor.css兩個文件。

  註意vendor.js僅在開發調試階段使用,所以並沒有對它進行壓縮,正式發佈並不需要執行vendorJs對象。

  該腳本的最後一行證明瞭這一點。

return isDev ? [ vendorJs, vendorCss] : [vendorCss];
  •  npm run app 

  npm run app又包含兩個命令,用於執行webpack.config.util.jswebpack.config.js

webpack --config webpack.config.util.js && webpack

  先來看看webpack.config.util.js

 1 const pathPlugin = require('path');
 2 const webpack = require('webpack');
 3 
 4 module.exports = (env) => {
 5     //是否開發環境
 6     const isDev = !(env && env.prod);
 7     const mode = isDev ? "development" : "production";
 8 
 9     //獲取路徑
10     function getPath(path) {
11         return pathPlugin.join(__dirname, path);
12     }
13 
14     //打包util腳本庫
15     return {
16         mode: mode,
17         entry: { util: [getPath("Typings/util/index.ts")] },
18         output: {
19             publicPath: 'dist/',
20             path: getPath("wwwroot/dist"),
21             filename: "[name].js",
22             library: '[name]'
23         },
24         resolve: {
25             extensions: ['.js', '.ts']
26         },
27         devtool: "source-map",
28         module: {
29             rules: [
30                 { test: /\.ts$/, use: ['awesome-typescript-loader?silent=true'] }
31             ]
32         },
33         plugins: [
34             new webpack.DllReferencePlugin({
35                 manifest: require('./wwwroot/dist/vendor-manifest.json')
36             }),
37             new webpack.DllPlugin({
38                 path: getPath("wwwroot/dist/[name]-manifest.json"),
39                 name: "[name]"
40             })
41         ]
42     }
43 }
webpack.config.util.js

  它將查找Util.Samples.Webs項目下Typings/util/index.ts文件,這是util預設導出文件,所有在外部需要訪問的類型都會從這裡導出。

  當webpack.config.util.js執行完畢,會在dist目錄創建util.js文件。

  同樣的,util.js文件僅用於開發調試階段 

  下麵看webpack.config.js

 1 const pathPlugin = require('path');
 2 const webpack = require('webpack');
 3 var Extract = require("extract-text-webpack-plugin");
 4 const AngularCompilerPlugin = require('@ngtools/webpack').AngularCompilerPlugin;
 5 
 6 module.exports = (env) => {
 7     //是否開發環境
 8     const isDev = !(env && env.prod);
 9     const mode = isDev ? "development" : "production";
10 
11     //將css提取到單獨文件中
12     const extractCss = new Extract("app.css");
13 
14     //獲取路徑
15     function getPath(path) {
16         return pathPlugin.join(__dirname, path);
17     }
18 
19     //打包js
20     let jsConfig = {
21         mode: mode,
22         entry: { app: getPath("Typings/main.ts") },
23         output: {
24             publicPath: 'dist/',
25             path: getPath("wwwroot/dist"),
26             filename: "[name].js",
27             chunkFilename: '[id].chunk.js'
28         },
29         resolve: {
30             extensions: ['.js', '.ts']
31         },
32         devtool: "source-map",
33         module: {
34             rules: [
35                 { test: /\.ts$/, use: isDev ? ['awesome-typescript-loader?silent=true', 'angular-router-loader'] : ['@ngtools/webpack'] },
36                 { test: /\.js$/, loader: '@angular-devkit/build-optimizer/webpack-loader', options: { sourceMap: false } },
37                 { test: /\.html$/, use: 'html-loader?minimize=false' }
38             ]
39         },
40         plugins: [
41             new webpack.DefinePlugin({
42                 'process.env': { NODE_ENV: isDev ? JSON.stringify("dev") : JSON.stringify("prod") }
43             })
44         ].concat(isDev ? [
45             new webpack.DllReferencePlugin({
46                 manifest: require('./wwwroot/dist/vendor-manifest.json')
47             }),
48             new webpack.DllReferencePlugin({
49                 manifest: require('./wwwroot/dist/util-manifest.json')
50             })
51         ] : [
52                 new AngularCompilerPlugin({
53                     tsConfigPath: 'tsconfig.json',
54                     entryModule: "Typings/app/app.module#AppModule"
55                 })
56             ])
57     }
58 
59     //打包css
60     let cssConfig = {
61         mode: mode,
62         entry: { app: getPath("wwwroot/css/main.scss") },
63         output: {
64             publicPath: './',
65             path: getPath("wwwroot/dist"),
66             filename: "[name].css"
67         },
68         resolve: {
69             modules: ['wwwroot']
70         },
71         devtool: "source-map",
72         module: {
73             rules: [
74                 {
75                     test: /\.scss$/, use: extractCss.extract({
76                         use: isDev ? ['css-loader', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] } }, 'sass-loader']
77                             : ['css-loader?minimize', { loader: 'postcss-loader', options: { plugins: [require('autoprefixer')] } }, 'sass-loader']
78                     })
79                 },
80                 {
81                     test: /\.(png|jpg|gif|woff|woff2|eot|ttf|svg)(\?|$)/, use: {
82                         loader: 'url-loader',
83                         options: {
84                             limit: 20000,
85                             name: "[name].[ext]",
86                             outputPath: "images/"
87                         }
88                     }
89                 }
90             ]
91         },
92         plugins: [
93             extractCss
94         ]
95     }
96     return [jsConfig, cssConfig];
97 }
webpack.config.js

  webpack.config.js查找Typings目錄下的main.ts,main.ts是angular項目的入口文件。

  webpack通過遞歸依賴查找main.ts,將除了util.js和vendor.js以外所有引用到的ts或js文件打包到dist/app.js文件中。

  註意,正式發佈時,app.js將採用angular官方提供的webpack編譯插件@ngtools/webpack進行AOT編譯並打包生成。

 

  現在dist目錄生成瞭如下文件。

  0.chunk.js是由angular子模塊生成的js文件,當路由配置對子模塊啟用了延遲載入,每個子模塊都會生成一個獨立的js文件。

 

  loadChildren以延遲載入的方式來配置SystemModule子模塊。

 運行機制

  現在運行angular應用的js文件已經就緒,讓我們把它運行起來,在VS上F5啟動項目。 

  註意:你應該使用Google Chrome來打開它,IE瀏覽器,可以通過啟用polyfill來勉強支持,不過由於效果不佳,我已經把它扔掉了。 

  當瀏覽器打開首頁http://localhost:5200,Asp.Net Core啟動文件Startup.cs中配置的預設路由將被激活,從而將請求發送到HomeController控制器的Index方法。

   Index方法直接返回了Views目錄下Index.cshtml首頁。

 

  environment標簽是一個環境判斷條件,用於設置開發及上線等不同階段的內容。

  <environment include="Development">用於開發階段,<environment exclude="Development">用於發佈階段,可以看出,在發佈後並不需要vendor.js和util.js文件,因為app.js會包含它們。 

  好,現在瀏覽器載入了Index首頁,Angular應用是如何運行起來的呢?

  • Angular的引導過程

   還記得Angular應用入口文件main.ts嗎,來看看它包含什麼內容。

 

  platformBrowserDynamic是為瀏覽器平臺提供的JIT動態編譯服務,它將引導AppModule根模塊的啟動。

  AppModule是Angular應用的根模塊,它的主要任務之一就是啟動AppComponent根組件。

  AppComponent是整個Angular應用的根組件,所有其它組件都將被載入到根組件中。

 

  selector用於指定組件的自定義標簽,這裡將根組件標簽定義為<app></app>,你發現它已經被放置在Index.cshtml中。 

  AppComponent根組件準備啟動了,由於是JIT編譯,所以它需要獲取視圖 

  組件的視圖由templateUrl屬性指定。

templateUrl: env.prod() ? './app.component.html' : '/home/main'

  我們希望開發階段通過訪問服務端控制器來獲取視圖,這樣在編輯TagHelper時就能更方便,只需刷新頁面就能看見效果。 

  env是一個環境檢測對象,prod方法如果返回true表明當前為正式環境,將從app.component.html靜態文件獲取視圖,如果是開發調試環境,則訪問服務端HomeController控制器的Main方法獲取視圖。 

  Main方法上的Html特性,是用來幫助.cshtml生成.html靜態文件的輔助工具。 

  一般情況下,你並不需要手工設置Html特性來生成html文件,Util提供了ViewControllerBase控制器基類,當你的視圖控制器繼承它,所有html文件就會生成到約定的目錄中。

 

  由Template屬性設置的路徑可知,Typings/app中的項目結構也採用模塊化組織,與區域模塊相對應。

   現在來看根組件的視圖。

   這是你第一次看見Util封裝的TagHelper標簽,以<util-打頭的標簽都是Util TagHelper,它們以粗體顯示,這是由於安裝了Resharper的原因。

  TagHelper在運行時會把html輸出到頁面,它們把弱類型的html封裝成了具有強類型提示的標簽。

  如何知道某個TagHelper到底輸出了什麼html呢?

  一種辦法是打開它生成的.html文件來查找,不過當頁面很複雜時,這種辦法有點吃力。

  另一種辦法是查看日誌,Util TagHelper的每個組件都提供了write-log屬性,當設置為true,就會在C盤log目錄生成日誌。

 

  main.cshtml視圖中最關鍵的部分就是<router-outlet></router-outlet>標簽。

  router-outlet是Angular路由的占位符,當根模塊AppModule中配置的路由激活時,相關的Angular組件就會被放進這個占位符中。

  根模塊中的路由配置被拆分到一個單獨的模塊AppRoutingModule中,路由配置如下。

 

  通過路由配置可以發現,當打開首頁時,命中路由第二項path:’’,會跳轉到/systems/application路徑,systems是一個子模塊,我們來查看它的路由配置。

 

 

  /systems/application將激活ApplicationIndexComponent組件,並把它載入到根組件的<router-outlet></router-outlet>中。

  ApplicationIndexComponent組件請求服務端地址/view/systems/application獲取視圖。

 

   /view打頭的地址將匹配到Areas區域控制器,這是在MVC路由配置中設置的。

 

  控制器ApplicationControllerIndex方法將返回視圖。

 

  Angular JIT編譯會在系統啟動時請求服務端URL,在Chrome瀏覽器F12調出開發者工具,刷新頁面,會觀察到頁面請求了Areas中的控制器。

  

  所以你在開發階段運行項目會感覺比較慢,在正式發佈後就沒這些開銷了。

小結  

  本文簡要介紹了Util Demo的目錄結構和運行機制,如果你沒有Angular基礎,估計還是很難看懂,建議你閱讀Angular中文網https://angular.cn  

  未完待續,下一篇將對Util Demo的Angular封裝進行介紹,本來是準備這篇介紹的,不過限於篇幅,放到下篇,我知道,太長的文章既難寫更難讀。

  寫文需要動力,請大家多多支持,點下推薦,Github點下星星。

  Util應用框架交流一群: 24791014


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

-Advertisement-
Play Games
更多相關文章
  • 【轉發】原文地址 https://www.cnblogs.com/dragonsuc/p/5512797.html mpstat -P ALL 和 sar -P ALL 說明:sar -P ALL > aaa.txt 重定向輸出內容到文件 aaa.txt 經常用來監控linux的系統狀況,比如cpu ...
  • iOS10 語音播報填坑詳解(解決串列播報中斷問題) 在來聊這類需求的解決方案之前,咱們還是先來聊一聊這類需求的真實使用場景:語音播報。語音播報需求運用最為廣泛的應該是收銀對賬了,就類似於支付寶、微信、收錢吧等的收款語音提示一樣。在iOS 10 之前,蘋果沒有提供通知擴展類的時候,如果想要實現殺進程 ...
  • Appium是一個開源測試自動化框架,用於本機、混合和移動Web應用程式,它使用WebDriver 協議驅動 iOS、Android和Windows應用程式。 ...
  • 這段時間開發了一個微信小程式,雖然小程式的導航API 官方文檔寫得很詳細,但是在具體開發過程中還是會遇到很多不明白,或者一時轉不過彎的地方。 1、頁面切換傳參,參數讀取 1.1 wx.navigateTo(Object) 功能:保留當前頁面,跳轉到應用內的某個頁面,但是不能跳到 tabbar 頁面。 ...
  • 開發現狀 隨著技術的發展,移動開發設備遍地開花,各種各樣的移動設備不如人們的家庭。移動設備不局限於手機、IPA等設備。由於Android開源,可在條款允許範圍內修改代碼二次開發,結合物聯網技術的迅速普及,各種Android應用進入物聯網之中。智能手機、智能電視等各種擁有交互需求的設備大部分都是基於A ...
  • 概述 ScreenMatch是根據你的需要,生成需要適配的尺寸的文件,手機會根據屏幕相關參數自動尋找合適的尺寸文件 添加插件 如圖,打開Android Studio的Settings設置,找到Plugins,點擊Browse Repositories,在彈出的輸入框里填入screenMatch,就可 ...
  • https://www.cnblogs.com/chenqf/p/6386163.html 前言 Http 緩存機製作為 web 性能優化的重要手段,對於從事 Web 開發的同學們來說,應該是知識體系庫中的一個基礎環節,同時對於有志成為前端架構師的同學來說是必備的知識技能。但是對於很多前端同學來說, ...
  • 1)名字VS值 名字和記憶體(存儲)位置相關聯。 名字—(環境)———>位置——(狀態)——>值 這兩個映射都在隨著程式的運行而改變。 2)環境VS狀態 環境是指一個名字到存儲位置映射,也可以說是名字到變數(左值)的映射,環境的改變需要遵守語言的作用於與規則; 狀態是一個從記憶體位置到它的值的映射,即把 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...