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

前言

 

每个人都有自己习惯的项目结构,有人的喜欢在项目里面建解决方案文件夹;有的人喜欢传统的三层命名;有的人喜欢单一,简单的项目一个 csproj 就搞定。

 

反正就是萝卜青菜,各有所爱。

 

可能不同的公司对这些会有特定的要求,也可能会随开发自己的想法去实践。

 

那么,问题就来了。如果有一个新项目,你会怎么去创建?

 

可能比较多的方式会是下面三种:

 

  • 简单粗暴型,打开 VS 就是右键添加,然后引入一堆包,每个项目添加引用。 
  • 脚本型,基于 dotnet cli,创建解决方案,创建项目,添加包,添加项目引用。 
  • 高大上型,VS项目模板,直接集成到 VS 上面了。

 

以前我也是基于 dotnet cli 写好了 sh 或 ps 的脚本,然后用这些脚本来生成新项目。

 

但是呢,这三种方式,始终都有不尽人意的地方。

 

因为建好的都是空模板,还要做一堆复杂的操作才可以让项目“正常”的跑起来。比如,这个公共类要抄过来,那个公共类要抄过来。。。这不是明摆着浪费时间嘛。。。

 

下面介绍一个小办法来帮大家省点时间。

 

基于 dotnet cli 创建自己的项目模板,也就是大家常说的脚手架。

 

dotnet cli项目模板预热

 

开始正题之前,我们先看一下 dotnet cli 自带的一些模板。

 

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

 

可以看到种类还是很多的,由于工作大部分时间都是在写 WebAPI,所以这里就用 WebAPI 来写个简单的模板。

 

下面我们就基于 dotnet cli 写一个自己的模板。

 

编写自己的模板

 

既然是模板,就肯定会有一个样例项目。

 

下面我们建一个样例项目,大致成这样,大家完全可以按照自己习惯来。

 

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

 

这其实就是一个普通的项目,里面添加了 NLog,Swagger,Dapper 等组件,各个项目的引用关系是建好的。

 

该有的公共类,里面也都包含了,好比宇内分享的那个 WebHostBuilderJexusExtensions。

 

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

 

下面是这个模板跑起来的效果。

 

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

 

就是一个简单的Swagger页面。

 

现在样例已经有了,要怎么把这个样例变成一个模板呢?

 

答案就是 template.json!

 

在样例的根目录创建一个文件夹.template.config,同时在这个文件夹下面创建 template.json。

 

示例如下:

 

{
    "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。

 

  • shortName,简写,偷懒必备,好比能写 -h 就绝对不写 –help 
  • sourceName,这是个可选的字段,它的值会替换指定的项目名,正常是把项目名赋值在这里。如果不指定,创建的项目就和样例项目保持一致。

 

在写完 template.json 之后,还需要安装一下这个模板到我们的 cli 中。

 

使用 dotnet new -i 进行模板的安装。

 

下面是安装示例。

 

dotnet new -i ./content/TplDemo

 

这里要注意的是,与.template.config 文件夹同级的目录,都会被打包进模板中。

 

在执行安装命令之后,就可以看到我们的模板已经安装好了。

 

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

 

这个时候已经迫不及待的想来试试这个模板了。

 

先来看看这个模板的帮助信息。

 

dotnet new tpl -h

 

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

 

因为我们目前还没有设置参数,所以这里显示的是还没有参数。

 

下面来创建一个项目试试。

 

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

 

从创建一个项目,到运行起来,很简单,效果也是我们预期的。

 

下面来看看,新建的这个 HelloTpl 这个项目的目录结构和我们的模板是否一样。

 

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

 

可以看到,除了名字,其他的内容都是一样的。

 

是不是感觉又可以少复制粘贴好多代码了。

 

虽说,现在建项目,已经能把一个大的模板完整的 copy 出来了,但是始终不是很灵活!

 

可能有小伙伴会问,明明已经很方便了呀,为什么还会说它不灵活呢?

 

且听我慢慢道来。

 

如果说这个模板是个大而全的模板,包含了中间件 A,中间件 B,中间件 C 等 N 个中间件!

 

而在建新项目的时候,已经明确了只用中间件 A,那么其他的中间件对我们来说,可能就没有太大的存在意义!

 

很多时候,不会想让这些多余的文件出现在代码中,有没有办法来控制呢?

 

答案是肯定的!可以把不需要的文件排除掉就可以了。

 

文件过滤

 

模板项目中有一个 RequestLogMiddleware,就用它来做例子。

 

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

 

我们只需要做下面几件事就可以了。

 

第一步,在 template.json 中添加过滤

 

加入一个名字为 EnableRequestLog 的 symbol。同时指定源文件

 

{
    "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" 
                  ]
              }
          ]
      }
    ]    
}

 

第二步,在模板的代码中做一下处理

 

主要是 Startup.cs,因为 Middleware 就是在这里启用的。

 

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?}");
        });
    }
}

 

这样的话,只要 EnableRequestLog 是 true,那么就可以包含这两段代码了。

 

下面更新一下已经安装的模板。

 

这个时候再去看它的帮助信息,已经可以看到我们加的参数了。

 

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

 

下面先建一个默认的(不启用 RequestLog)

 

dotnet new tpl -n NoLog

 

这个命令等价于

 

dotnet new tpl -n WithLog -E false

 

下面是建好之后的目录结构和 Startup.cs

 

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

 

可以看到 RequestLog 相关的东西都已经不见了。

 

再建一个启用 RequestLog 的,看看是不是真的起作用了。

 

dotnet new tpl -n WithLog -E true

 

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

 

可以看到,效果已经出来了。

 

下面在介绍一个比较有用的特性。动态切换,这个其实和上面介绍的内容相似。

 

动态切换

 

直接举个例子来说明吧。

 

假设我们的模板支持 MSSQL, MySQL, PgSQL 和 SQLite 四种数据库操作

 

在新建一个项目的时候,只需要其中一种,好比说要建一个 PgSQL 的,肯定就不想看到其他三种。

 

这里不想看到,有两个地方,一个是 nuget 包的引用,一个是代码。

 

上一小节是对某个具体的功能进行了开关的操作,这里有了 4 个,我们要怎么处理呢?

 

我们可以用类型是 choice 的参数来完成这个操作。

 

修改 template.json,加入下面的内容

 

{
  "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 这个参数了。

 

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

 

下面分别创建一个 MsSQL 和 PgSQL 的项目,用来对比和验证。

 

先后执行

 

dotnet new tpl -n MsSQLTest -s MsSQL 

dotnet new tpl -n PgSQLTest -s PgSQL

 

然后打开对应的 csproj

 

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

 

可以看到,PgSQL 的,添加多了 NPgsql 这个包。而 MsSQL 的却没有。

 

同样的,DapperRepositoryBase 也是一样的效果。在创建 Connection对象的时候,都根据模板来生成了。

 

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

 

当然这个是在我们自己本地安装的模板,其他人是没有办法使用的。

 

如果想公开,可以发布到 nuget 上面去。如果是在公司内部共享,可以搭建一个内部的 nuget 服务,将模板上传到内部服务器里面去。

 

下面是一些可以开箱即用的模板:

 

https://dotnetnew.azurewebsites.net/

 

总结

 

有一个自己的项目模板(脚手架),还是很方便的。

 

一建生成自己需要的东西,减少了不必要的代码复制,可以将更多精力放在业务实现上。

 

在平时还是要有一些积累,当积累足够丰富之后,我们的脚手架可能就会变得十分强大。

 

最后是文中的示例代码

 

Template:https://github.com/catcherwong/Demos/tree/master/src/Template

 

转自:Catcher8  cnblogs.com/catcher1994/p/10061470.html

 

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...