一、文件夹\项目结构
1.1、文件夹
net6.0:针对.net 6.0 项目模板
net6.0pack:针对net6.0打包
1.2、项目结构
Web\WebApi多项目、各层项目、单元测试项目
目标:制作Web\WebApi两个项目模板
二、模板参数
2.1、template\net6.0\.template.config\template.json
{ "$schema": "http://json.schemastore.org/template", "author": "yinyunpan", "classifications": [ "Web", "WebApi" ], "name": "项目模板案例", "identity": "sample.template", "shortName": "st", "tags": { "language": "C#", //解决方案,与HostIdentifier用法生成解决方案文件名呼应 "type": "solution" }, "sourceName": "sampletemplate", "preferNameDirectory": false, //重新生成项目guid,对应template.sln中项目guid "guids": [ "5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3", "30B12CFC-11E1-4E1C-B484-F708CEBD080A", "810CD02B-0E13-4123-A84F-FBD4F4F1CD3A", "3D2A540B-E773-44FA-82CF-14F3393D41B2", "F4C1EFA8-824C-4985-A50D-BD1171ED1CFC", "41C74675-6234-41F2-9B8A-4D06159D2A81", "5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C", "8A522BE9-0EE5-4C54-861B-394452A6744E", "F075845E-43F4-466D-B731-904782BD9047" ], "symbols": { /** template.sln文件有用host,排除项目文件引用 modifiers-condition,排除文件夹+文件 **/ "host": { "type": "parameter", "datatype": "choice", "choices": [ { "choice": "web", "description": "后台" }, { "choice": "webapi", "description": "接口" } ], "defaultValue": "web", "description": "项目类型", "displayName": "项目类型", "isRequired": true }, "web": { "type": "computed", "value": "(host == \"web\")" }, "webapi": { "type": "computed", "value": "(host == \"webapi\")" }, //应用的环境变量 "appUK": { "type": "parameter", "datatype": "string", "description": "应用标识", "displayName": "应用标识", "isRequired": true, "replaces": "wutong.netcore.sampletemplate" }, "kestrelHttpPort": { "type": "parameter", "datatype": "integer", "description": "启动配置文件的http端口", "displayName": "http端口" }, "kestrelHttpPortGenerated": { "type": "generated", "generator": "port", "parameters": { "low": 5000, "high": 5300 } }, "kestrelHttpPortReplacer": { "type": "generated", "generator": "coalesce", "parameters": { "sourceVariableName": "kestrelHttpPort", "fallbackVariableName": "kestrelHttpPortGenerated" }, //与launchSettings.json端口一致,才能随机生成替换 "replaces": "5000" }, //模板主机标识 "HostIdentifier": { "type": "bind", "binding": "HostIdentifier" } }, "sources": [ { "exclude": [ ".template.config/**/*", ".vs/**/*" ], "modifiers": [ { "condition": "(web)", "exclude": [ "src/WebApi/**/*", "test/WebApiUnitTest/**/*" ] }, { "condition": "(webapi)", "exclude": [ "src/Web/**/*", "test/WebUnitTest/**/*" ] }, { /** visual studio创建模板会自动生成新的解决方案文件.sln、与模板下{sourceName}.sln冲突,导致生成visual studio提醒外部文件变化重新加载,操作不友好 判断使用模板主机标识:vs-visual studio、dotnetcli\dotnetcli-preview-命令窗口 模板下解决方案文件名要非{sourceName}.sln,命令窗口重命名 参考:https://github.com/sayedihashimi/template-sample/tree/main/src/Samples/06-console-csharp-fsharp **/ "condition": "(HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\")", "rename": { "template.sln": "sampletemplate.sln" } } ] } ] }
总结:
host定义项目类型,方便使用在host基础上定义web、webapi计算型布尔变量。
项目开发中本地启动文件(launchSettings.json)中会经常用环境变量,如:应用标识。
模板文件中关键字替换依赖环境变量的replaces参数,需要注意保证replaces的值与文件被替换的值一样,如:http端口、应用标识。
根据项目类型,排除web\webapi对应的项目以及单元测试项目文件。
如果需要区分当前使用模板场景(visual studio 可视化窗口、命令行窗口),有不同执行行为,可以使用HostIdentifier变量。如:解决文件.sln生成。
2.2、template\net6.0\.template.config\ide.host.json
{ "$schema": "http://json.schemastore.org/vs-2017.3.host", "order": 200, "icon": "ide/sampletemplate.png", "symbolInfo": [ { "id": "host", "isVisible": true }, { "id": "appUK", "isVisible": true }, //隐藏http端口,自动生成 { "id": "kestrelHttpPort", "isVisible": false } ] }
2.3、template\net6.0\.template.config\dotnetcli.host.json
{ "$schema": "http://json.schemastore.org/dotnetcli.host", "symbolInfo": { "host": { "isHidden": false }, "appUK": { "isHidden": false }, //隐藏http端口,自动生成 "kestrelHttpPort": { "isHidden": true } } }
三、解决方案文件设置
Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32414.318 MinimumVisualStudioVersion = 10.0.40219.1 <!--#if(web)--> Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "src\Web\Web.csproj", "{F075845E-43F4-466D-B731-904782BD9047}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebUnitTest", "test\WebUnitTest\WebUnitTest.csproj", "{8A522BE9-0EE5-4C54-861B-394452A6744E}" EndProject <!--#endif--> <!--#if(webapi)--> Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApi", "src\WebApi\WebApi.csproj", "{5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiUnitTest", "test\WebApiUnitTest\WebApiUnitTest.csproj", "{5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C}" EndProject <!--#endif--> Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "src\Shared\Shared.csproj", "{30B12CFC-11E1-4E1C-B484-F708CEBD080A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain.Interfaces", "src\Domain.Interfaces\Domain.Interfaces.csproj", "{810CD02B-0E13-4123-A84F-FBD4F4F1CD3A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Domain.Service", "src\Domain.Service\Domain.Service.csproj", "{3D2A540B-E773-44FA-82CF-14F3393D41B2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.Interfaces", "src\Infrastructure.Interfaces\Infrastructure.Interfaces.csproj", "{F4C1EFA8-824C-4985-A50D-BD1171ED1CFC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.Implements", "src\Infrastructure.Implements\Infrastructure.Implements.csproj", "{41C74675-6234-41F2-9B8A-4D06159D2A81}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution <!--#if(web)--> {F075845E-43F4-466D-B731-904782BD9047}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F075845E-43F4-466D-B731-904782BD9047}.Debug|Any CPU.Build.0 = Debug|Any CPU {F075845E-43F4-466D-B731-904782BD9047}.Release|Any CPU.ActiveCfg = Release|Any CPU {F075845E-43F4-466D-B731-904782BD9047}.Release|Any CPU.Build.0 = Release|Any CPU {8A522BE9-0EE5-4C54-861B-394452A6744E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A522BE9-0EE5-4C54-861B-394452A6744E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A522BE9-0EE5-4C54-861B-394452A6744E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A522BE9-0EE5-4C54-861B-394452A6744E}.Release|Any CPU.Build.0 = Release|Any CPU <!--#endif--> <!--#if(webapi)--> {5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A6E6B9F-F8FA-4152-95B4-860F8CCAE3C3}.Release|Any CPU.Build.0 = Release|Any CPU {5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {5EAD3BC3-554B-4058-9A9D-A91DF5F2DE1C}.Release|Any CPU.Build.0 = Release|Any CPU <!--#endif--> {30B12CFC-11E1-4E1C-B484-F708CEBD080A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {30B12CFC-11E1-4E1C-B484-F708CEBD080A}.Debug|Any CPU.Build.0 = Debug|Any CPU {30B12CFC-11E1-4E1C-B484-F708CEBD080A}.Release|Any CPU.ActiveCfg = Release|Any CPU {30B12CFC-11E1-4E1C-B484-F708CEBD080A}.Release|Any CPU.Build.0 = Release|Any CPU {810CD02B-0E13-4123-A84F-FBD4F4F1CD3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {810CD02B-0E13-4123-A84F-FBD4F4F1CD3A}.Debug|Any CPU.Build.0 = Debug|Any CPU {810CD02B-0E13-4123-A84F-FBD4F4F1CD3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {810CD02B-0E13-4123-A84F-FBD4F4F1CD3A}.Release|Any CPU.Build.0 = Release|Any CPU {3D2A540B-E773-44FA-82CF-14F3393D41B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3D2A540B-E773-44FA-82CF-14F3393D41B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D2A540B-E773-44FA-82CF-14F3393D41B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D2A540B-E773-44FA-82CF-14F3393D41B2}.Release|Any CPU.Build.0 = Release|Any CPU {F4C1EFA8-824C-4985-A50D-BD1171ED1CFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F4C1EFA8-824C-4985-A50D-BD1171ED1CFC}.Debug|Any CPU.Build.0 = Debug|Any CPU {F4C1EFA8-824C-4985-A50D-BD1171ED1CFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4C1EFA8-824C-4985-A50D-BD1171ED1CFC}.Release|Any CPU.Build.0 = Release|Any CPU {41C74675-6234-41F2-9B8A-4D06159D2A81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {41C74675-6234-41F2-9B8A-4D06159D2A81}.Debug|Any CPU.Build.0 = Debug|Any CPU {41C74675-6234-41F2-9B8A-4D06159D2A81}.Release|Any CPU.ActiveCfg = Release|Any CPU {41C74675-6234-41F2-9B8A-4D06159D2A81}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2A1729E7-0ED9-48D5-84EB-897B28B15D78} EndGlobalSection EndGlobal
总结:
根据项目类型,保留对应项目引用。
四、开发测试
4.1、打包项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <PackageType>Template</PackageType> <PackageVersion>1.0.0.1</PackageVersion> <PackageId>sample.template</PackageId> <Title>项目模板案例</Title> <Authors>flightengine</Authors> <Description>项目模板案例</Description> <PackageTags>dotnet-new;templates</PackageTags> <TargetFramework>net6.0</TargetFramework> <IncludeContentInPack>true</IncludeContentInPack> <IncludeBuildOutput>false</IncludeBuildOutput> <ContentTargetFolders>content</ContentTargetFolders> </PropertyGroup> <ItemGroup> <!--PackagePath="content" 保持文件夹\文件的层级结构,否则所有文件都在根目录--> <Content Include="..\net6.0\**\*" PackagePath="content" Exclude="..\net6.0\**\bin\**;..\net6.0\**\obj\**;..\net6.0\**\.vs\**" /> <Compile Remove="..\net6.0\**\*" /> </ItemGroup> </Project>
总结:
PackagePath定义很重要
4.2、编译打包
net6.0pack文件夹下执行:
dotnet pack
4.3、安装包
dotnet new --install sample.template.1.0.0.1.nupkg
生成到 net6.0pack\bin\Debug 文件夹
4.4、卸载包
dotnet new --uninstall sample.template
开发中修改后,先执行卸载再安装模板,后vs或者cli测试。
4.5、安装
visual studio:
勾选避免生成的代码层次与模板项目定义不一致。
4.6、上传
执行批处理文件
@echo off :: 解决中文乱码 chcp 65001 set PackageVersion="1.0.0.1" del .\PublishNuget\*.nupkg dotnet clean .\net6.0pack.csproj dotnet pack .\net6.0pack.csproj -p:PackageVersion=%PackageVersion% -c Release -o .\PublishNuget :: dotnet nuget push 上传包源服务器 pause
生成到 net6.0pack\PublishNuget 文件夹,可以再结合上传包源服务器命令。
五、案例源码
https://github.com/yinyunpan/template
六、参考
6.1、官方或者第三方包参考
https://dotnetnew.azurewebsites.net/
搜索包,然后到 https://www.nuget.org/ 下载,分析别人是如何实现的。
6.2、文档
https://github.com/dotnet/templating/wiki/Reference-for-template.json
官网文档各个参数含义,有些特殊参数注释\案例写不是很明白。
https://github.com/sayedihashimi/template-sample
实际开发中遇到各种问题场景的分析及其解决办法,总结的很不错。
标签:
留言评论