每一個人都有本身習慣的項目結構,有人的喜歡在項目裏面建解決方案文件夾;有的人喜歡傳統的三層命名;有的人喜歡單一,簡單的項目一個csproj就搞定。。git
反正就是蘿蔔青菜,各有所愛。github
可能不一樣的公司對這些會有特定的要求,也可能會隨開發本身的想法去實踐。web
那麼,問題就來了。若是有一個新項目,你會怎麼去建立?sql
可能比較多的方式會是下面三種:數據庫
之前我也是基於dotnet cli寫好了sh或ps的腳本,而後用這些腳原本生成新項目。json
可是呢,這三種方式,始終都有不盡人意的地方。服務器
由於建好的都是空模板,還要作一堆複雜的操做纔可讓項目「正常」的跑起來。好比,這個公共類要抄過來,那個公共類要抄過來。。。這不是明擺着浪費時間嘛。。。app
下面介紹一個小辦法來幫你們省點時間。ide
基於dotnet cli建立本身的項目模板,也就是你們常說的腳手架。ui
開始正題以前,咱們先看一下dotnet cli自帶的一些模板。
能夠看到種類仍是不少的,因爲工做大部分時間都是在寫WebAPI,因此這裏就用WebAPI來寫個簡單的模板。
下面咱們就基於dotnet cli寫一個本身的模板。
既然是模板,就確定會有一個樣例項目。
下面咱們建一個樣例項目,大體成這樣,你們徹底能夠按照本身習慣來。
這其實就是一個普通的項目,裏面添加了NLog,Swagger,Dapper等組件,各個項目的引用關係是建好的。
該有的公共類,裏面也都包含了,比如宇內分享的那個WebHostBuilderJexusExtensions。
下面是這個模板跑起來的效果。
就是一個簡單的Swagger頁面。
如今樣例已經有了,要怎麼把這個樣例變成一個模板呢?
答案就是template.json
!
在樣例的根目錄建立一個文件夾.template.config
,同時在這個文件夾下面建立template.json
。
示例以下:
{ "author": "Catcher Wong", //必須 "classifications": [ "Web/WebAPI" ], //必須,這個對應模板的Tags "name": "TplDemo", //必須,這個對應模板的Templates "identity": "TplDemoTemplate", //可選,模板的惟一名稱 "shortName": "tpl", //必須,這個對應模板的Short Name "tags": { "language": "C#" , "type":"project" }, "sourceName": "TplDemo", // 可選,要替換的名字 "preferNameDirectory": true // 可選,添加目錄 }
在這裏,有幾個比較重要的東西,一個是shortName,一個是sourceName。
-h
就絕對不寫 --help
在寫完template.json
以後,還須要安裝一下這個模板到咱們的cli中。
使用 dotnet new -i
進行模板的安裝。
下面是安裝示例。
dotnet new -i ./content/TplDemo
這裏要注意的是,與.template.config
文件夾同級的目錄,都會被打包進模板中。
在執行安裝命令以後,就能夠看到咱們的模板已經安裝好了。
這個時候已經火燒眉毛的想來試試這個模板了。
先來看看這個模板的幫助信息。
dotnet new tpl -h
由於咱們目前尚未設置參數,因此這裏顯示的是尚未參數。
下面來建立一個項目試試。
從建立一個項目,到運行起來,很簡單,效果也是咱們預期的。
下面來看看,新建的這個HelloTpl這個項目的目錄結構和咱們的模板是否同樣。
能夠看到,除了名字,其餘的內容都是同樣的。
是否是感受又能夠少複製粘貼好多代碼了。
雖然說,如今建項目,已經能把一個大的模板完整的copy出來了,可是始終不是很靈活!
可能有小夥伴會問,明明已經很方便了呀,爲何還會說它不靈活呢?
且聽我慢慢道來。
若是說這個模板是個大而全的模板,包含了中間件A,中間件B,中間件C等N箇中間件!
而在建新項目的時候,已經明確了只用中間件A,那麼其餘的中間件對咱們來講,可能就沒有太大的存在乎義!
不少時候,不會想讓這些多餘的文件出如今代碼中,有沒有辦法來控制呢?
答案是確定的!能夠把不須要的文件排除掉就能夠了。
模板項目中有一個RequestLogMiddleware
,就用它來作例子。
咱們只須要作下面幾件事就能夠了。
第一步,在template.json
中添加過濾
加入一個名字爲EnableRequestLog
的symbol。同時指定源文件
{ "author": "Catcher Wong", //others... "symbols":{ //是否啓用RequestLog這個Middleware "EnableRequestLog": { "type": "parameter", //它是參數 "dataType":"bool", //bool類型的參數 "defaultValue": "false" //默認是不啓用 } }, "sources": [ { "modifiers": [ { "condition": "(!EnableRequestLog)", //條件,由EnableRequestLog參數決定 "exclude": [ //排除下面的文件 "src/TplDemo/Middlewares/RequestLogMiddleware.cs", "src/TplDemo/Middlewares/RequestLogServiceCollectionExtensions.cs" ] } ] } ] }
第二步,在模板的代碼中作一下處理
主要是Startup.cs
,由於Middleware就是在這裏啓用的。
using System; //other using... using TplDemo.Core; #if (EnableRequestLog) using TplDemo.Middlewares; #endif /// <summary> /// /// </summary> public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //other code.... #if (EnableRequestLog) //request Log app.UseRequestLog(); #endif app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } }
這樣的話,只要EnableRequestLog
是true,那麼就能夠包含這兩段代碼了。
下面更新一下已經安裝的模板。
這個時候再去看它的幫助信息,已經能夠看到咱們加的參數了。
下面先建一個默認的(不啓用RequestLog)
dotnet new tpl -n NoLog
這個命令等價於
dotnet new tpl -n WithLog -E false
下面是建好以後的目錄結構和Startup.cs
能夠看到RequestLog相關的東西都已經不見了。
再建一個啓用RequestLog的,看看是否是真的起做用了。
dotnet new tpl -n WithLog -E true
能夠看到,效果已經出來了。
下面在介紹一個比較有用的特性。動態切換,這個其實和上面介紹的內容類似。
直接舉個例子來講明吧。
假設咱們的模板支持MSSQL, MySQL, PgSQL和SQLite四種數據庫操做
在新建一個項目的時候,只須要其中一種,比如說要建一個PgSQL的,確定就不想看到其餘三種。
這裏不想看到,有兩個地方,一個是nuget包的引用,一個是代碼。
上一小節是對某個具體的功能進行了開關的操做,這裏有了4個,咱們要怎麼處理呢?
咱們能夠用類型是choice
的參數來完成這個操做。
修改template.json
,加入下面的內容
{ "author": "Catcher Wong", //others "symbols":{ "sqlType": { "type": "parameter", "datatype": "choice", "choices": [ { "choice": "MsSQL", "description": "MS SQL Server" }, { "choice": "MySQL", "description": "MySQL" }, { "choice": "PgSQL", "description": "PostgreSQL" }, { "choice": "SQLite", "description": "SQLite" } ], "defaultValue": "MsSQL", "description": "The type of SQL to use" }, "MsSQL": { "type": "computed", "value": "(sqlType == \"MsSQL\")" }, "MySQL": { "type": "computed", "value": "(sqlType == \"MySQL\")" }, "PgSQL": { "type": "computed", "value": "(sqlType == \"PgSQL\")" }, "SQLite": { "type": "computed", "value": "(sqlType == \"SQLite\")" } } }
看了上面的JSON內容以後,相信你們也知道個因此然了。有一個名爲sqlType的參數,它有幾中數據庫選擇,默認是MsSQL。
還另外定義了幾個計算型的參數,它的取值是和sqlType的值息息相關的。
MsSQL,MySQL,PgSQL和SQLite這4個參數也是咱們在代碼裏要用到的!!
修改csproj文件,讓它能夠根據sqlType來動態引用nuget包,咱們加入下面的內容
<ItemGroup Condition="'$(MySQL)' == 'True' "> <PackageReference Include="MySqlConnector" Version="0.47.1" /> </ItemGroup> <ItemGroup Condition="'$(PgSQL)' == 'True' "> <PackageReference Include="Npgsql" Version="4.0.3" /> </ItemGroup> <ItemGroup Condition="'$(SQLite)' == 'True' "> <PackageReference Include="Microsoft.Data.Sqlite" Version="2.1.0" /> </ItemGroup>
一樣的,代碼也要作相應的處理
#if (MsSQL) using System.Data.SqlClient; #elif (MySQL) using MySql.Data.MySqlClient; #elif (PgSQL) using Npgsql; #else using Microsoft.Data.Sqlite; #endif protected DbConnection GetDbConnection() { #if (MsSQL) return new SqlConnection(_connStr); #elif (MySQL) return new MySqlConnection(_connStr); #elif (PgSQL) return new NpgsqlConnection(_connStr); #else return new SqliteConnection(_connStr); #endif }
修改好以後,一樣要去從新安裝這個模板,安裝好以後,就能夠看到sqlType這個參數了。
下面分別建立一個MsSQL和PgSQL的項目,用來對比和驗證。
前後執行
dotnet new tpl -n MsSQLTest -s MsSQL dotnet new tpl -n PgSQLTest -s PgSQL
而後打開對應的csproj
能夠看到,PgSQL的,添加多了NPgsql
這個包。而MsSQL的卻沒有。
一樣的,DapperRepositoryBase也是同樣的效果。在建立Connection對象的時候,都根據模板來生成了。
固然這個是在咱們本身本地安裝的模板,其餘人是沒有辦法使用的。
若是想公開,能夠發佈到nuget上面去。若是是在公司內部共享,能夠搭建一個內部的nuget服務,將模板上傳到內部服務器裏面去。
下面是一些能夠開箱即用的模板:
https://dotnetnew.azurewebsites.net/
有一個本身的項目模板(腳手架),仍是很方便的。
一建生成本身須要的東西,減小了沒必要要的代碼複製,能夠將更多精力放在業務實現上。
在平時仍是要有一些積累,當積累足夠豐富以後,咱們的腳手架可能就會變得十分強大。
參考文檔
dotnet new下面默認的模板 https://github.com/aspnet/Templating
templating的源碼 https://github.com/dotnet/templating
template.json的說明 https://github.com/dotnet/templating/wiki/Reference-for-template.json
dotnet cli的文檔 https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet?tabs=netcore21
最後是文中的示例代碼