在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言 每个人都有自己习惯的项目结构,有人的喜欢在项目里面建解决方案文件夹;有的人喜欢传统的三层命名;有的人喜欢单一,简单的项目一个csproj就搞定。。 反正就是萝卜青菜,各有所爱。 可能不同的公司对这些会有特定的要求,也可能会随开发自己的想法去实践。 那么,问题就来了。如果有一个新项目,你会怎么去创建? 可能比较多的方式会是下面三种:
以前我也是基于dotnet cli写好了sh或ps的脚本,然后用这些脚本来生成新项目。 但是呢,这三种方式,始终都有不尽人意的地方。 因为建好的都是空模板,还要做一堆复杂的操作才可以让项目“正常”的跑起来。比如,这个公共类要抄过来,那个公共类要抄过来。。。这不是明摆着浪费时间嘛。。。 下面介绍一个小办法来帮大家省点时间。 基于dotnet cli创建自己的项目模板,也就是大家常说的脚手架。 dotnet cli项目模板预热 开始正题之前,我们先看一下dotnet cli自带的一些模板。 可以看到种类还是很多的,由于工作大部分时间都是在写WebAPI,所以这里就用WebAPI来写个简单的模板。 下面我们就基于dotnet cli写一个自己的模板。 编写自己的模板 既然是模板,就肯定会有一个样例项目。 下面我们建一个样例项目,大致成这样,大家完全可以按照自己习惯来。 这其实就是一个普通的项目,里面添加了NLog,Swagger,Dapper等组件,各个项目的引用关系是建好的。 该有的公共类,里面也都包含了,好比宇内分享的那个WebHostBuilderJexusExtensions。 下面是这个模板跑起来的效果。 就是一个简单的Swagger页面。 现在样例已经有了,要怎么把这个样例变成一个模板呢? 答案就是 在样例的根目录创建一个文件夹 示例如下: { "author": "Catcher Wong", //必须 "classifications": [ "Web/WebAPI" ], //必须,这个对应模板的Tags "name": "TplDemo", //必须,这个对应模板的Templates "identity": "TplDemoTemplate", //可选,模板的唯一名称 "shortName": "tpl", //必须,这个对应模板的Short Name "tags": { "language": "C#" , "type":"project" }, "sourceName": "TplDemo", // 可选,要替换的名字 "preferNameDirectory": true // 可选,添加目录 } 在这里,有几个比较重要的东西,一个是shortName,一个是sourceName。
在写完 使用 下面是安装示例。 dotnet new -i ./content/TplDemo 这里要注意的是,与 在执行安装命令之后,就可以看到我们的模板已经安装好了。 这个时候已经迫不及待的想来试试这个模板了。 先来看看这个模板的帮助信息。 dotnet new tpl -h 因为我们目前还没有设置参数,所以这里显示的是还没有参数。 下面来创建一个项目试试。 从创建一个项目,到运行起来,很简单,效果也是我们预期的。 下面来看看,新建的这个HelloTpl这个项目的目录结构和我们的模板是否一样。 可以看到,除了名字,其他的内容都是一样的。 是不是感觉又可以少复制粘贴好多代码了。 虽说,现在建项目,已经能把一个大的模板完整的copy出来了,但是始终不是很灵活! 可能有小伙伴会问,明明已经很方便了呀,为什么还会说它不灵活呢? 且听我慢慢道来。 如果说这个模板是个大而全的模板,包含了中间件A,中间件B,中间件C等N个中间件! 而在建新项目的时候,已经明确了只用中间件A,那么其他的中间件对我们来说,可能就没有太大的存在意义! 很多时候,不会想让这些多余的文件出现在代码中,有没有办法来控制呢? 答案是肯定的!可以把不需要的文件排除掉就可以了。 文件过滤 模板项目中有一个 我们只需要做下面几件事就可以了。 第一步,在 加入一个名字为 { "author": "Catcher Wong", //others... "symbols":{ //是否启用RequestLog这个Middleware "EnableRequestLog": { "type": "parameter", //它是参数 "dataType":"bool", //bool类型的参数 "defaultValue": "false" //默认是不启用 } }, "sources": [ { "modifiers": [ { "condition": "(!EnableRequestLog)", //条件,由EnableRequestLog参数决定 "exclude": [ //排除下面的文件 "src/TplDemo/Middlewares/RequestLogMiddleware.cs", "src/TplDemo/Middlewares/RequestLogServiceCollectionExtensions.cs" ] } ] } ] } 第二步,在模板的代码中做一下处理 主要是 using System; //other using... using TplDemo.Core; #if (EnableRequestLog) using TplDemo.Middlewares; #endif /// <summary> /// /// </summary> public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //other code.... #if (EnableRequestLog) //request Log app.UseRequestLog(); #endif app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } 这样的话,只要 下面更新一下已经安装的模板。 这个时候再去看它的帮助信息,已经可以看到我们加的参数了。 下面先建一个默认的(不启用RequestLog) dotnet new tpl -n NoLog 这个命令等价于 dotnet new tpl -n WithLog -E false 下面是建好之后的目录结构和Startup.cs 可以看到RequestLog相关的东西都已经不见了。 再建一个启用RequestLog的,看看是不是真的起作用了。 dotnet new tpl -n WithLog -E true 可以看到,效果已经出来了。 下面在介绍一个比较有用的特性。动态切换,这个其实和上面介绍的内容相似。 动态切换 直接举个例子来说明吧。 假设我们的模板支持MSSQL, MySQL, PgSQL和SQLite四种数据库操作 在新建一个项目的时候,只需要其中一种,好比说要建一个PgSQL的,肯定就不想看到其他三种。 这里不想看到,有两个地方,一个是nuget包的引用,一个是代码。 上一小节是对某个具体的功能进行了开关的操作,这里有了4个,我们要怎么处理呢? 我们可以用类型是 修改 { "author": "Catcher Wong", //others "symbols":{ "sqlType": { "type": "parameter", "datatype": "choice", "choices": [ { "choice": "MsSQL", "description": "MS SQL Server" }, { "choice": "MySQL", "description": "MySQL" }, { "choice": "PgSQL", "description": "PostgreSQL" }, { "choice": "SQLite", "description": "SQLite" } ], "defaultValue": "MsSQL", "description": "The type of SQL to use" }, "MsSQL": { "type": "computed", "value": "(sqlType == \"MsSQL\")" }, "MySQL": { "type": "computed", "value": "(sqlType == \"MySQL\")" }, "PgSQL": { "type": "computed", "value": "(sqlType == \"PgSQL\")" }, "SQLite": { "type": "computed", "value": "(sqlType == \"SQLite\")" } } } 看了上面的JSON内容之后,相信大家也知道个所以然了。有一个名为sqlType的参数,它有几中数据库选择,默认是MsSQL。 还另外定义了几个计算型的参数,它的取值是和sqlType的值息息相关的。 MsSQL,MySQL,PgSQL和SQLite这4个参数也是我们在代码里要用到的!! 修改csproj文件,让它可以根据sqlType来动态引用nuget包,我们加入下面的内容 <ItemGroup Condition="'$(MySQL)' == 'True' "> <PackageReference Include="MySqlConnector" Version="0.47.1" /> </ItemGroup> <ItemGroup Condition="'$(PgSQL)' == 'True' "> <PackageReference Include="Npgsql" Version="4.0.3" /> </ItemGroup> <ItemGroup Condition="'$(SQLite)' == 'True' "> <PackageReference Include="Microsoft.Data.Sqlite" Version="2.1.0" /> </ItemGroup> 同样的,代码也要做相应的处理 #if (MsSQL) using System.Data.SqlClient; #elif (MySQL) using MySql.Data.MySqlClient; #elif (PgSQL) using Npgsql; #else using Microsoft.Data.Sqlite; #endif protected DbConnection GetDbConnection() { #if (MsSQL) return new SqlConnection(_connStr); #elif (MySQL) return new MySqlConnection(_connStr); #elif (PgSQL) return new NpgsqlConnection(_connStr); #else return new SqliteConnection(_connStr); #endif } 修改好之后,同样要去重新安装这个模板,安装好之后,就可以看到sqlType这个参数了。 下面分别创建一个MsSQL和PgSQL的项目,用来对比和验证。 先后执行 dotnet new tpl -n MsSQLTest -s MsSQL dotnet new tpl -n PgSQLTest -s PgSQL 然后打开对应的csproj 可以看到,PgSQL的,添加多了 同样的,DapperRepositoryBase也是一样的效果。在创建Connection对象的时候,都根据模板来生成了。 当然这个是在我们自己本地安装的模板,其他人是没有办法使用的。 如果想公开,可以发布到nuget上面去。如果是在公司内部共享,可以搭建一个内部的nuget服务,将模板上传到内部服务器里面去。 下面是一些可以开箱即用的模板:https://dotnetnew.azurewebsites.net/ 总结 有一个自己的项目模板(脚手架),还是很方便的。 一建生成自己需要的东西,减少了不必要的代码复制,可以将更多精力放在业务实现上。 在平时还是要有一些积累,当积累足够丰富之后,我们的脚手架可能就会变得十分强大。 参考文档 dotnet new下面默认的模板 https://github.com/aspnet/Templating templating的源码 https://github.com/dotnet/templating template.json的说明 https://github.com/dotnet/templating/wiki/Reference-for-template.json dotnet cli的文档 https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet?tabs=netcore21 最后是文中的示例代码 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持极客世界。 |
请发表评论