C#創(chuàng)建一個(gè)可快速重復(fù)使用的項(xiàng)目模板(詳細(xì)過(guò)程)
寫在前面
其實(shí)很多公司或者資深的開發(fā)都有自己快速創(chuàng)建項(xiàng)目的腳手架的,有的是魔改代碼生成器實(shí)現(xiàn),有的直接基于T4,RazorEngine等模板引擎打造;但無(wú)論如何,其最終目的其實(shí)就是搭建一個(gè)自定義項(xiàng)目模板(腳手架)。
今天我們聊聊:如何基于官方的cli donet new 命令創(chuàng)建自己的項(xiàng)目模板。
什么是項(xiàng)目模板
我想用一個(gè)命令來(lái)說(shuō)明:
dotnet new list

到這里大家就非常熟悉了,原來(lái)大家平時(shí)創(chuàng)建項(xiàng)目都是基于已有的模板創(chuàng)建的(紅圈部分大家應(yīng)該不陌生);我們今天目的就是創(chuàng)建一個(gè)這樣的模板,并在vs新建項(xiàng)目時(shí)可供選擇創(chuàng)建項(xiàng)目,或者使用cli命令直接創(chuàng)建;
當(dāng)然,還有公開模板:
https://dotnetnew.azurewebsites.net/
創(chuàng)建自己的模板
1、先準(zhǔn)備好一個(gè)項(xiàng)目
這里準(zhǔn)備的項(xiàng)目就是平時(shí)普通的項(xiàng)目,后面會(huì)以這個(gè)項(xiàng)目為藍(lán)本創(chuàng)建模板;因?yàn)槲易罱褂肁zure Function類型項(xiàng)目比較多,我就以Function項(xiàng)目為例,其他類型項(xiàng)目同理的;
項(xiàng)目結(jié)構(gòu)圖:

項(xiàng)目文件結(jié)構(gòu):
D:.
│ appsettings.CI.json
│ appsettings.Development.json
│ appsettings.json
│ appsettings.Production.json
│ Dockerfile
│ Function1.cs
│ host.json
│ local.settings.json
│ MyCompany.Cutapi.FunctionTemp.csproj #這個(gè)名字后面要被替換的
│ settings.CI.yaml
│ settings.Production.yaml
│ Startup.cs
│
├─build
│ CD.yaml
│ CI.yaml
│ _deploy.yaml
│
└─deploy
│ kustomization.yaml
│
├─base
│ deploy.yaml
│ kustomization.yaml
│
├─ci
│ deploy.yaml
│ kustomization.yaml
│
└─prod
deploy.yaml
kustomization.yaml可以看到其實(shí)有很多跟構(gòu)建,部署等有關(guān)的配置文件;
Function1.cs
#模板項(xiàng)目的命名空間
namespace MyCompany.Cutapi.FunctionTemp
{
public class Function1
{
private readonly Stopwatch _sw;
private readonly IExtractSegmentService _extractSegmentService;
private readonly ILogger<Function1> _logger;
public Function1(IExtractSegmentService extractSegmentService, ILogger<Function1> logger)
{
_sw = new Stopwatch();
_extractSegmentService = extractSegmentService;
_logger = logger;
}
#模板項(xiàng)目的FunctionName 和一些跟隊(duì)列有關(guān)的配置,這些后面都要
[FunctionName("function1")]
[return: ServiceBus("cutapi-queue1-notify", Connection = "ServiceBusConnection")]
public async Task<VideoTranscodeNotify> Run([ServiceBusTrigger("cutapi-queue1", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message
, string messageId
, ServiceBusMessageActions messageActions
, Int32 deliveryCount
, DateTime enqueuedTimeUtc
, ILogger log
)
{
_sw.Start();
var messageBody = Encoding.UTF8.GetString(message.Body);
log.LogInformation($"{Environment.MachineName} -> function1 begin ->{messageId}: {messageBody}");
await messageActions.CompleteMessageAsync(message);
var result = new VideoTranscodeNotify();
try
{
//todo...
}
catch (Exception ex)
{
log.LogError(ex, $"{Environment.MachineName} -> {messageId}:function1 Exception:{ex.Message}");
}
_sw.Stop();
log.LogInformation($"{Environment.MachineName} function1 Over ->{messageId} Elapsed: {_sw.Elapsed}");
return result;
}
}
}以這個(gè)文件為例,模板項(xiàng)目里很多文件內(nèi)容都可以按自定義參數(shù)被替換;當(dāng)然文件名也可以替換;
2、創(chuàng)建配置文件
在項(xiàng)目根目錄下創(chuàng)建配置文件:/.template.config/template.json
結(jié)構(gòu)如下:
├─.template.config
│ template.json
內(nèi)容:
{
"author": "Heiner Wang", //作者
"classifications": [ "Azure Functions" ], //項(xiàng)目歸類 classifications 還會(huì)出現(xiàn)在“Tags”列中
"name": "Heiner Function", //項(xiàng)目全名,用戶應(yīng)看到的模板名稱。
"identity": "HeinerFunction", //項(xiàng)目唯一id
"shortName": "hfunc", //項(xiàng)目簡(jiǎn)寫
"tags": {
"language": "C#",
"type": "project"
},
"sourceName": "MyCompany.Cutapi.FunctionTemp", //運(yùn)行模板時(shí)使用 -n 或 --name 選項(xiàng)提供要替換的值,不寫了話項(xiàng)目名稱不變
"preferNameDirectory": true, //創(chuàng)建項(xiàng)目的目錄層級(jí);
"symbols": { //自定義語(yǔ)法
//自定義參數(shù),新項(xiàng)目命名空間
"Namespace": {
"type": "parameter",
"dataType": "text", //文本類型
"defaultValue": "Heiner.Function",
"replaces": "MyCompany.Cutapi.FunctionTemp" //項(xiàng)目里這個(gè)值將會(huì)被替換掉
//"fileRename": "MyCompany.Cutapi.FunctionTemp" //也可以指定替換文件名
},
"FunctionName": {
"type": "parameter",
"dataType": "text",
"defaultValue": "function1",
"replaces": "function1"
},
"QueueName": {
"type": "parameter",
"dataType": "text",
"defaultValue": "cutapi-queue1",
"replaces": "cutapi-queue1"
},
"EnableRedis": {
"type": "parameter",
"dataType": "bool", #布爾類型的
"defaultValue": "true"
}
}
}更多參數(shù)請(qǐng)參考:https://github.com/dotnet/templating/wiki/Reference-for-template.json
代碼段過(guò)濾
cs文件
//EnableRedis是自定義參數(shù)
#if (EnableRedis)
ConnectionMultiplexer redisConnection = ConnectionMultiplexer.Connect(AppSettings.GetConnectionString("Redis"));
builder.Services.AddSingleton<IConnectionMultiplexer>(redisConnection);
builder.Services.AddSingleton<IDatabase>(c => redisConnection.GetDatabase());
#endif項(xiàng)目文件
<ItemGroup> <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" /> <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.DurableTask" Version="2.9.0" /> <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="5.9.0" /> <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" /> </ItemGroup> <ItemGroup Condition="'$(EnableRedis)' == 'True' "> <PackageReference Include="StackExchange.Redis" Version="2.6.48" /> </ItemGroup>
模板文件加入如下配置
???????
"symbols":{...},
"sources": [
{
"modifiers": [
{
"condition": "(!EnableRedis)", //EnableRedis!=true
"exclude": [ //排除下面的文件(這里僅做示例),后面的模板項(xiàng)目當(dāng)設(shè)置參數(shù):EnableRedis==false時(shí),下面的文件就被過(guò)濾掉了
"src/MyCompany.Cutapi.FunctionTemp/Redis.cs",
]
}
]
}
] ???????3、執(zhí)行模板安裝
這一步是將根據(jù)配置文件,將普通項(xiàng)目安裝成一個(gè)項(xiàng)目模板,理論上創(chuàng)建自定義模板到這步就完成了;
項(xiàng)目根目錄執(zhí)行:
dotnet new install . 這里命令后面的`.` 是安裝當(dāng)前目錄的項(xiàng)目的意思; dotnet new install D:\MyCompany.Cutapi.FunctionTemp 也可以這樣,用絕對(duì)路徑
更新模板
強(qiáng)制覆蓋安裝
dotnet new install . --force
先刪除再安裝
#先刪除 dotnet new uninstall . #重新安裝 dotnet new install .
后面的.都代表在項(xiàng)目根目錄執(zhí)行,后面不再贅述;
4、檢查安裝結(jié)果
dotnet new list


無(wú)論用cli還是vs 都可以看到我們項(xiàng)目模板了,創(chuàng)建模板成功;
參考
5、推送到nuget服務(wù)端(可選)
這步是可選的! 注意!很多內(nèi)部模板要脫密處理后再執(zhí)行推送,請(qǐng)勿將機(jī)密信息推送到公網(wǎng);
1、模板項(xiàng)目根目錄創(chuàng)建文件MyCompany.Cutapi.FunctionTemp.nuspec
<?xml version="1.0"?> <package > <metadata> <id>HeinerFunction</id> <version>1.0.0</version> <authors>Heiner Wang</authors> <owners>Heiner Wang</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>xxx 公司 Azure Function 快速模板.</description> <tags>dotnet-new;template</tags> </metadata> <files> <file src="**\*" target="content"/> </files> </package>
???????2、生成nuget包
在項(xiàng)目根目錄執(zhí)行
nuget pack MyCompany.Cutapi.FunctionTemp.nuspec
生成nuget包:
HeinerFunction.1.0.0.nupkg
3、推送到服務(wù)端
nuget push HeinerFunction.1.0.0.nupkg -Source https://api.nuget.org/v3/index.json -ApiKey YOUR_API_KEY
這步的--Source參數(shù),如果你有搭建好自己的nuget服務(wù)端的話改成你自己的;
如何使用一個(gè)模板
模板有了,怎么用這個(gè)就簡(jiǎn)單了;
vs使用
在創(chuàng)建項(xiàng)目時(shí)直接選擇自定義模板

不過(guò)這樣的話,自定義參數(shù)都是用默認(rèn)值,所以我還是更推薦用命令行方式;
命令行使用(推薦)
大家做demo的時(shí)候都應(yīng)該執(zhí)行過(guò)這樣的命令,其實(shí)這就是使用了官方shotname為console的模板
dotnet new console -n MyConsoleApp1
一樣,自定義模板命令為:
#默認(rèn)參數(shù) dotnet new hfunc -n MyCompany.Heiner.Test #指定參數(shù) dotnet new hfunc -n MyCompany.Heiner.Test --Namespace MyCompany.Heiner.Test --FunctionName function-live-record --QueueName cutapi-live-record --EnableRedis false
創(chuàng)建成功

[參考]
https://learn.microsoft.com/zh-cn/dotnet/core/tools/custom-templates
https://cloud.tencent.com/developer/article/2319366
https://github.com/dotnet/templating/wiki/Reference-for-template.json
到此這篇關(guān)于C#如何創(chuàng)建一個(gè)可快速重復(fù)使用的項(xiàng)目模板的文章就介紹到這了,更多相關(guān)C#如何創(chuàng)建一個(gè)可快速重復(fù)使用的項(xiàng)目模板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# 實(shí)現(xiàn)特殊字符快速轉(zhuǎn)碼
這篇文章主要介紹了C# 實(shí)現(xiàn)特殊字符快速轉(zhuǎn)碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
C#實(shí)現(xiàn)拆分合并Word表格中的單元格
我們?cè)谑褂肳ord制作表格時(shí),由于表格較為復(fù)雜,只是簡(jiǎn)單的插入行、列并不能滿足我們的需要。要做一個(gè)完整的表格,很多時(shí)候需要將單元格進(jìn)行拆分或者合并。本文將詳細(xì)為您介紹在Word表格中拆分或合并單元格的思路及方法,希望對(duì)大家有所幫助2022-12-12
C#?設(shè)置Chart的X軸為時(shí)間軸???????詳情
這篇文章主要介紹了C#設(shè)置Chart的X軸為時(shí)間軸???????詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08
C#窗口實(shí)現(xiàn)定時(shí)關(guān)機(jī)系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C#窗口實(shí)現(xiàn)定時(shí)關(guān)機(jī)系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
C# TabControl手動(dòng)觸發(fā)DrawItem的實(shí)現(xiàn)
本文主要介紹了C# TabControl手動(dòng)觸發(fā)DrawItem的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
C#實(shí)現(xiàn)的算24點(diǎn)游戲算法實(shí)例分析
這篇文章主要介紹了C#實(shí)現(xiàn)的算24點(diǎn)游戲算法,實(shí)例分析了算24點(diǎn)游戲相關(guān)的運(yùn)算技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
C#訪問(wèn)PostGreSQL數(shù)據(jù)庫(kù)的方法
這次的項(xiàng)目中的一個(gè)環(huán)節(jié)要求我把PostGreSQL數(shù)據(jù)取出來(lái),然后放到SqlServer里,再去處理分析。2013-04-04

