由于工作需要,研究了一下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。

primaryOutputs参数自定义输出,这里只定义了项目名。

postActions则是定义模板生成后的动作,支持的Action参考文档

更多介绍(如生成Guid,随机数等)参考Wiki

示例文件内容:

{
"$schema": "http://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": "http://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": "http://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命令即可。

最终效果

示例代码

dotnet templating 定制自己的项目模板的更多相关文章

  1. 打造自己的.NET Core项目模板

    前言 每个人都有自己习惯的项目结构,有人的喜欢在项目里面建解决方案文件夹:有的人喜欢传统的三层命名:有的人喜欢单一,简单的项目一个csproj就搞定.. 反正就是萝卜青菜,各有所爱. 可能不同的公司对 ...

  2. NET Core项目模板

    打造自己的.NET Core项目模板 https://www.cnblogs.com/catcher1994/p/10061470.html 前言 每个人都有自己习惯的项目结构,有人的喜欢在项目里面建 ...

  3. 使用 DotNet CLI 创建自定义的 WPF 项目模板

    描述 当我们安装完 DotNetCore 3.0 版本的 SDK 后,我们就可以创建基于 DotNetCore 的 WPF 项目模板,通过如下 CLI 可以方便快捷的创建并运行我们的项目: dotne ...

  4. 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

    标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...

  5. asp.net core web 解决方案多项目模板制作打包总结

    一.文件夹\项目结构 1.1.文件夹 net6.0:针对.net 6.0 项目模板 net6.0pack:针对net6.0打包 1.2.项目结构 Web\WebApi多项目.各层项目.单元测试项目 目 ...

  6. Cookiecutter: 更好的项目模板工具:(1)简介及可用资源汇总

    原文档地址:https://cookiecutter.readthedocs.io/en/latest/ 本系列只介绍cookiecutter的基础使用,而且会删除与功能使用无关的部分.深度使用及了解 ...

  7. 使用 .NET CORE 创建 项目模板,模板项目,Template

    场景:日常工作中,你可能会碰到需要新建一个全新的解决方案的情况(如公司新起了一个新项目,需要有全新配套的后台程序),如果公司内部基础框架较多.解决方案需要DDD模式等,那么从新起项目到各种依赖引用到能 ...

  8. Worker Services的新项目模板

    .NET Core3.0创建Worker Services2019-10-24 09:05  成天  阅读(1438)  评论(20)  编辑收藏 .NET CORE 3.0新增了Worker Ser ...

  9. vs项目模板创建和使用

    一.使用dotnet命令创建(适用于.NET Core,可以创建包含任意数量个项目的模板,但不会出现在vs的新建项目模板中) 官方文档:https://docs.microsoft.com/zh-cn ...

随机推荐

  1. 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 百篇博客分析OpenHarmony源码 | v13.02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  2. 超简洁,玩转springboot 之springboot自定义start工程

    springboot 的start 建一个父工程 不需要其他目录结构,需要注意的是把type的类型改为POM 这样就没有工程的目录结构 因为父工程不需要 给父工程的pom添加依赖 <depend ...

  3. mysql面试题及答案,mysql最新面试题,mysql面试题大全汇总

    mysql最新面试题及答案汇总 Mysql 的存储引擎,myisam和innodb的区别.mysql最新面试题 答: 1.MyISAM 是非事务的存储引擎,适合用于频繁查询的应用.表锁,不会出现死锁, ...

  4. react之组件数据挂在方式

    1.属性(props) 组件间传值,在React中是通过只读属性 props 来完成数据传递的. props:接受任意的入参,并返回用于描述页面展示内容的 React 元素. import React ...

  5. React Native之新架构中的Turbo Module实现原理分析

    有段时间没更新博客了,之前计划由浅到深.从应用到原理,更新一些RN的相关博客.之前陆续的更新了6篇RN应用的相关博客(传送门),后边因时间问题没有继续更新.主要是平时空余时间都用来帮着带娃了,不过还是 ...

  6. NOI 2021 部分题目题解

    最近几天复盘了一下NOI 2021,愈发发觉自己的愚蠢,可惜D2T3仍是不会,于是只写前面的题解 Day1 T1 可以发现,每次相当于将 \(x\to y\) 染上一种全新颜色,然后一条边是重边当且仅 ...

  7. 题解 「BZOJ3636」教义问答手册

    题目传送门 Description 作为泉岭精神的缔造者.信奉者.捍卫者.传承者,Pear决定印制一些教义问答手册,以满足泉岭精神日益增多的信徒.Pear收集了一些有关的诗选.语录,其中部分内容摘录在 ...

  8. 题解 「2017 山东一轮集训 Day5」苹果树

    题目传送门 题目大意 给出一个 \(n\) 个点的图,每个点都有一个权值 \(f_i\) ,如果 \(f_i=-1\) 表示 \(i\) 这个点是坏的.定义一个点是有用的当且仅当它不是坏的,并且它连的 ...

  9. javascript-jquery的基本方法

    1.去除字符串中两端的空格$.trim(str) var str1=" 123 " $.trim(str1);//123 2.遍历对象的数据并进行操作$.each(obj,func ...

  10. 【Java虚拟机7】ClassLoader源码文档翻译

    前言 学习JVM类加载器,ClassLoader这个类加载器的核心类是必须要重视的. Notes:下方蓝色文字是自己的翻译(如果有问题请指正).黑色文字是源文档.红色文字是自己的备注. ClassLo ...