写在前面

其实很多公司或者资深的开发都有自己快速创建项目的脚手架的,有的是魔改代码生成器实现,有的直接基于T4,RazorEngine等模板引擎打造;但无论如何,其最终目的其实就是搭建一个自定义项目模板(脚手架)。

今天我们聊聊:如何基于官方的cli donet new 命令创建自己的项目模板。

什么是项目模板

我想用一个命令来说明:

dotnet new list

到这里大家就非常熟悉了,原来大家平时创建项目都是基于已有的模板创建的(红圈部分大家应该不陌生);我们今天目的就是创建一个这样的模板,并在vs新建项目时可供选择创建项目,或者使用cli命令直接创建;

当然,还有公开模板:

https://dotnetnew.azurewebsites.net/

创建自己的模板

1、先准备好一个项目

这里准备的项目就是平时普通的项目,后面会以这个项目为蓝本创建模板;因为我最近使用Azure Function类型项目比较多,我就以Function项目为例,其他类型项目同理的;

项目结构图:

项目文件结构:

D:.
│ appsettings.CI.json
│ appsettings.Development.json
│ appsettings.json
│ appsettings.Production.json
│ Dockerfile
│ Function1.cs
│ host.json
│ local.settings.json
│ MyCompany.Cutapi.FunctionTemp.csproj #这个名字后面要被替换的
│ 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

可以看到其实有很多跟构建,部署等有关的配置文件;

Function1.cs

#模板项目的命名空间
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;
} #模板项目的FunctionName 和一些跟队列有关的配置,这些后面都要
[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;
}
}
}

以这个文件为例,模板项目里很多文件内容都可以按自定义参数被替换;当然文件名也可以替换;

2、创建配置文件

在项目根目录下创建配置文件:/.template.config/template.json

结构如下:

├─.template.config

│ template.json

内容:

{
"author": "Heiner Wang", //作者
"classifications": [ "Azure Functions" ], //项目归类 classifications 还会出现在“Tags”列中
"name": "Heiner Function", //项目全名,用户应看到的模板名称。
"identity": "HeinerFunction", //项目唯一id
"shortName": "hfunc", //项目简写
"tags": {
"language": "C#",
"type": "project"
},
"sourceName": "MyCompany.Cutapi.FunctionTemp", //运行模板时使用 -n 或 --name 选项提供要替换的值,不写了话项目名称不变
"preferNameDirectory": true, //创建项目的目录层级;
"symbols": { //自定义语法
//自定义参数,新项目命名空间
"Namespace": {
"type": "parameter",
"dataType": "text", //文本类型
"defaultValue": "Heiner.Function",
"replaces": "MyCompany.Cutapi.FunctionTemp" //项目里这个值将会被替换掉
//"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"
}
}
}

更多参数请参考:https://github.com/dotnet/templating/wiki/Reference-for-template.json

代码段过滤

cs文件

//EnableRedis是自定义参数
#if (EnableRedis)
ConnectionMultiplexer redisConnection = ConnectionMultiplexer.Connect(AppSettings.GetConnectionString("Redis"));
builder.Services.AddSingleton<IConnectionMultiplexer>(redisConnection);
builder.Services.AddSingleton<IDatabase>(c => redisConnection.GetDatabase());
#endif

项目文件

<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": [ //排除下面的文件(这里仅做示例),后面的模板项目当设置参数:EnableRedis==false时,下面的文件就被过滤掉了
"src/MyCompany.Cutapi.FunctionTemp/Redis.cs",
]
}
]
}
]

3、执行模板安装

这一步是将根据配置文件,将普通项目安装成一个项目模板,理论上创建自定义模板到这步就完成了;

项目根目录执行:

dotnet new install .
这里命令后面的`.` 是安装当前目录的项目的意思; dotnet new install D:\MyCompany.Cutapi.FunctionTemp
也可以这样,用绝对路径

更新模板

强制覆盖安装

dotnet new install . --force

先删除再安装

#先删除
dotnet new uninstall . #重新安装
dotnet new install .

后面的.都代表在项目根目录执行,后面不再赘述;

4、检查安装结果

dotnet new list

无论用cli还是vs 都可以看到我们项目模板了,创建模板成功;

参考

5、推送到nuget服务端(可选)

这步是可选的! 注意!很多内部模板要脱密处理后再执行推送,请勿将机密信息推送到公网;

1、模板项目根目录创建文件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包

在项目根目录执行

nuget pack MyCompany.Cutapi.FunctionTemp.nuspec

生成nuget包:

HeinerFunction.1.0.0.nupkg

3、推送到服务端

nuget push HeinerFunction.1.0.0.nupkg  -Source https://api.nuget.org/v3/index.json -ApiKey YOUR_API_KEY

这步的--Source参数,如果你有搭建好自己的nuget服务端的话改成你自己的;

如何使用一个模板

模板有了,怎么用这个就简单了;

vs使用

在创建项目时直接选择自定义模板

不过这样的话,自定义参数都是用默认值,所以我还是更推荐用命令行方式;

命令行使用(推荐)

大家做demo的时候都应该执行过这样的命令,其实这就是使用了官方shotname为console的模板

 dotnet new console -n MyConsoleApp1

一样,自定义模板命令为:

#默认参数
dotnet new hfunc -n MyCompany.Heiner.Test #指定参数
dotnet new hfunc -n MyCompany.Heiner.Test --Namespace MyCompany.Heiner.Test --FunctionName function-live-record --QueueName cutapi-live-record --EnableRedis false

创建成功

[参考]

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

C#如何创建一个可快速重复使用的项目模板的更多相关文章

  1. 创建一个可用的简单的SpringMVC项目,图文并茂

    转载麻烦注明下来源:http://www.cnblogs.com/silentdoer/articles/7134332.html,谢谢. 最近在自学SpringMVC,百度了很多资料都是比较老的,而 ...

  2. 记录心得-IntelliJ iDea 创建一个maven管理的的javaweb项目

    熟能生巧,还是记录一下吧~ 开始! 第一步:File--New--Project--Maven--Create from archetype--maven-archetype-webapp 第二步:解 ...

  3. 入职一个月快速熟悉大型Vue项目经验感想

    来到和睦的公司家庭已经一个月出头了,从技术层面来说,公司项目PC端是我目前来说接触的最大最复杂的项目了,德老师也说这个不断开发更新迭代的项目的代码量相对于全国的web来说是蛮多的,对于快速熟悉这样的大 ...

  4. 创建一个netcore2.0和angular的项目并运行起来

    netcore2.0发布了,喜大普奔. 我们先下载SDK,请看张善友老师的这篇博客 http://www.cnblogs.com/shanyou/p/7363037.html 下载完之后 我用的vs2 ...

  5. 创建一个本地CocoaPods库 并在项目中使用该库

    1.新建一个项目如下 2.往TestLib中添加两个文件 3.终端进入TestLib 生成git文件 然后提交到本地 git init git add . git commit -m '添加perso ...

  6. IDEA创建一个Spring MVC 框架Java Web项目,Gradle构建

    注:此篇有些细节没写出,此文主要写重要的环节和需要注意的地方,轻喷 新建项目 选择Gradle , 勾选java 和 web.之后就是设定项目路径和名称,这里就不啰嗦了. build.gradle文件 ...

  7. 使用Vaadin的maven archetype创建一个空Vaadin项目

    所在公司要求使用这个臭屎粑粑一样的Vaadin,我也没办法.为了更好地开展工作,对得起老板发给我的工资,就算是臭屎粑粑,也要尽力给他玩儿出花样来. Vaadin针对Eclipse和Netbeans等I ...

  8. 【Spring】创建一个Spring的入门程序

    3.创建一个Spring的入门程序 简单记录 - Java EE企业级应用开发教程(Spring+Spring MVC+MyBatis)- Spring的基本应用 Spring与Spring MVC的 ...

  9. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  10. Vue Create 创建一个新项目 命令行创建和视图创建

    Vue Create 创建一个新项目 命令行创建和视图创建 开始之前 你可以先 >>:cd desktop[将安装目录切换到桌面] >>:vue -V :Vue CLI 3.0 ...

随机推荐

  1. dotnet 8 破坏性改动 在 AssemblyInformationalVersionAttribute 添加上 git 的 commit 号

    我在一个 WPF 项目里面,在界面显示应用的版本号,更新到 dotnet 8 的 SDK 之后,发现我的界面布局损坏了.本质上这个破坏性改动和 WPF 没有什么关系,是 dotnet 的 SDK 或编 ...

  2. dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息

    在 WPF 触摸应用中,插入触摸设备,即可在应用里面使用上插入的触摸设备.在 WPF 使用触摸设备的触摸时,需要获取到触摸设备的信息,才能实现触摸 获取触摸设备插入 在 WPF 中,通过 Window ...

  3. centos7虚拟机部署netcore3.1服务供局域网访问

    如果买了亚马逊.腾讯.阿里等服务器,基本上几分钟就可以跑aspnetcore,外网访问分分钟.但是便宜点的服务器访问速度就没那么理想.这时候就需要考虑零成本的虚拟机部署了,当然这个基本都是局域网做测试 ...

  4. 16、数据库加固-mongo 加固

    1.指定日志与数据库存放位置 在配置文件中设置指向目录位置 自建配置文件:vim /usr/local/mongodb/etc/mongodb.conf dbpath=/data/db logpath ...

  5. uniapp获取用户信息

    新接口getUserProfileFn内置login,如果必须要login返回的参数要隔离开 vue3书写要对按钮配置属性 <button @click="logintou" ...

  6. 使用 Python 旋转PDF页面、或调整PDF页面顺序

    在将纸质文档扫描成PDF电子文档时,有时可能会出现页面方向翻转或者页面顺序混乱的情况.为了确保更好地浏览和查看PDF文件,本文将分享一个使用Python来旋转PDF页面或者调整PDF页面顺序的解决方案 ...

  7. 用 C 语言开发一门编程语言 — Q-表达式

    目录 文章目录 目录 前文列表 Q-表达式 读取并存储输入 实现 Q-Expression 语法解析器 读取 Q-Expression 实现 Q-Expression 的函数 Head & T ...

  8. CCL 2024 Task7 双任务冠军

    近期参加NLP领域CCL2024评测,现将赛题背景和实现方法分享,推理文本纠错领域的发展. 1.背景信息 随着教育的发展和网络的普及,作文评价的规模越来越大,人工评改作文的成本和效率成为一大难题.为了 ...

  9. Flask源码阅读

    上下文篇 整个Flask生命周期中都依赖LocalStack()栈?.而LocalStack()分为请求上下文_request_ctx_stack和应用上下文_app_ctx_stack. _requ ...

  10. 动态类型语言 VS 静态类型语言

    一. 运行期动态修改类型结构 动态编程语言是高级编程语言的一个类别,在计算机科学领域已被广泛应用.它是一类在运行时可以改变其结构的语言:例如新的函数.对象.甚至代码可以被引进,已有的函数可以被删除或是 ...