dotnet templating 訂製自己的項目模板
由於工作需要,研究了一下VS 項目模板生成的相關內容,本文做一下記錄藉助 .NET Core Template Engine創建一個加單的項目模板。
創建項目程式碼和配置文件
首先創建一個Minimal 版本的Web Api項目,基於這個項目定義自己的項目模板。Template Engine 脫離$safeprojectname$這種項目模板參數的方式,好處是模板項目本身始終是一個可以正常編譯運行的項目,可以及時發現製作模板過程中的問題。
.template.config介紹
在項目根目錄下創建.template.config文件夾,存在項目相關配置文件。文件夾內創建三個json文件
- template.json 必須的模板配置文件,定義一些模板配置資訊以及模板生成的邏輯等
- ide.host.json 非必須的VS介面配置文件,定義模板圖標以及配置展示參數等
- dotnetcli.host.json 非必須的Cli配置文件,定義命令中參數的短名稱
官方文檔中給了template.json文件必須參數的說明:

除了這三個文件還可以增加多語言的支援,具體用法可以參考aspnetcore的項目模板
修改.template.config下的文件
template.json
對symbols欄位做一些說明,其他必須參數上面的圖片中有說明。這裡我定義了四個參數:Framework,UseAuthorize,SeparateRouteHandler,IsLast。Framework和IsLast只作演示,UseAuthorize,SeparateRouteHandler兩個參數是訂製模板時需要用到的參數。
Framework參數的datatype為choice類型,vs中會展示一個下拉列表。replaces的指定當前我們選擇的值替換項目中的哪個值,類似sourceName。其他欄位均為字面意思。
UseAuthorize,SeparateRouteHandler兩個類型為bool類型,UseAuthorize是否引入認證鑒權,演示程式碼中的判斷。SeparateRouteHandler演示模板的文件排除邏輯,生成項目時根據該值判斷排除多餘文件。
IsLast參數則是簡單的演示計算參數。
sources參數用來自定義輸出源,定義modifiers通過condition指定一個條件包含或者排除哪些文件,本文使用的是exclude。
postActions則是定義模板生成後的動作,支援的Action參考文檔。
更多介紹(如生成Guid,隨機數等)參考Wiki
示例文件內容:
{ "$schema": "//json.schemastore.org/template", "author": "Stacking", "classifications": ["web", "api"], "identity": "stacking.web.tmpl", "name": "Stacking Web Api", "shortName": "stack.tmpl", "tags": { "language": "C#", "type": "project" }, "sourceName": "Stacking.Web", "preferNameDirectory": true, "sources": [ { "modifiers": [ { "condition": "(!SeparateRouteHandler)", "exclude": ["DemoHandler.cs"] } ] } ], "symbols": { "Framework": { "type": "parameter", "description": "The target .net framework for the project.", "datatype": "choice", "choices": [ { "choice": "net6.0", "description": "Target net6.0" }, { "choice": "net5.0", "description": "Target net5.0" } ], "replaces": "net6.0", "defaultValue": "net6.0" }, "UseAuthorize": { "type": "parameter", "datatype": "bool", "defaultValue": "false", "description": "this application add authorize." }, "SeparateRouteHandler": { "type": "parameter", "datatype": "bool", "defaultValue": "false", "description": "route handler use a separate file." }, "IsLast": { "type": "computed", "value": "(Framework == \"net6.0\")" } }, "primaryOutputs": [ { "path": "Stacking.Web.csproj" } ], "postActions": [ { "description": "Restore NuGet packages required by this project.", "manualInstructions": [ { "text": "Run 'dotnet restore'" } ], "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", "continueOnError": true } ] }
exclude指定路徑數組支援Dic/** 以及Dic/*.cs的方式
ide.host.json
該文件只有兩個簡單的定義,通過icon指定模板圖標(相對路徑),symbolInfo定義參數資訊,如vs上顯示哪些參數以及參數說明和默認值等。
示例文件內容:
{ "$schema": "//json.schemastore.org/vs-2017.3.host", "icon": "WebAPI.png", "symbolInfo": [ { "id": "Framework", "name": { "text": ".net framework" }, "description": { "text": ".net framework for the project" }, "isVisible": true, "defaultValue": "net6.0" }, { "id": "UseAuthorize", "name": { "text": "the project use authorize" }, "isVisible": true }, { "id": "SeparateRouteHandler", "name": { "text": "separate route handler file" }, "invertBoolean": false, "isVisible": true } ] }
invertBoolean參數的值為true時會將bool類型參數的值反轉,如SeparateRouteHandler的值在指定為true時,模板的判斷邏輯程式碼中這個值會變為false。
dotnetcli.host.json
cli的json文件中只定義了一個symbolInfo參數,定義命令行方式創建模板時參數的短名稱。
示例文件內容:
{ "$schema": "//json.schemastore.org/dotnetcli.host", "symbolInfo": { "Framework": { "longName": "framework" }, "UseAuthorize": { "longName": "use-authorize", "shortName": "au" }, "SeparateRouteHandler": { "longName": "separate-route-handler", "shortName": "separate" } } }
自定義模板
自定義CS文件
cs文件自定義使用類似預編譯指令的方式 ,使用#if,#endif等命令自定義輸出邏輯
#if (SeparateRouteHandler) using Stacking.Web; #endif #if(UseAuthorize) using Microsoft.AspNetCore.Authentication.JwtBearer; #endif
如果判斷流程複雜且模板文件變動較大可以參考aspnetcore官網模板實例,定義兩個獨立文件再通過rename和exclude控制模板輸出內容。
{ "condition": "(UseMinimalAPIs && (IndividualAuth || OrganizationalAuth))", "rename": { "Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs": "Program.cs" }, "exclude": [ "Program.MinimalAPIs.WindowsOrNoAuth.cs" ] }
自定義csproj文件
csproj文件使用注釋的方式,判斷參數控制邏輯
<!--#if(UseAuthorize)--> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" /> <!--#endif-->
自定義json文件
json文件同樣使用注釋的方式
//#if(EnableOpenAPI) "launchUrl": "swagger", //#else "launchUrl": "weatherforecast", //#endif
安裝、卸載模板
在項目根目錄下執行dotnet new -i ./ 命令安裝自定義的模板。
卸載模板命令:dotnet new -u ./ 如果不是模板目錄則需要指定模板全路徑
模板發布到nuget不再說明,寫不動了。參考官網模板定義一個templates.nuspec文件,執行nuget.exe pack命令即可。
最終效果


