CodeSmith 代码生成工具

CodeSmith 代码生成工具

简介

CodeSmith 是一种基于模板的代码生成工具,它使用类似于 ASP.NET 的语法来生成任意类型的代码或文本。

在线文档 https://www.w3cschool.cn/codesmith/

使用手册 http://www.doc88.com/p-5751215590812.html

准备工作

下载codesmith

前往 https://www.codesmithtools.com/ 下载codesmith8

配置数据库

打开codesmith,选择右侧的Schema Explorer,添加数据源

codesmith配置数据源

  1. 选择除了SqlServer的数据库时,连接报错 .Net Framework Data Provider,说明没有对应的数据库连接驱动,需要下载,此处以MySql为例

  2. 前往 https://dev.mysql.com/downloads/windows/visualstudio/ 下载 MySql.Data.dll,下载zip格式即可,下载完成,将文件解压,然后将 MySql.Data.dll 复制到codesmi安装目录的 bin 文件下

  3. 修改 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\machine.config,因为权限问题可能无法修改,建议使用 notepad++ 打开编辑

  4. 找到 system.data 下的 DbProviderFactories 节点,并做如下更改

<system.data>
	<DbProviderFactories>
    <!-- 添加如下两项 -->
	<remove invariant="MySql.Data.MySqlClient" />
	<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient"description=".Net Framework Data Provider for MySQL" 
			type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data,Version=8.0.18, Culture=neutral,PublicKeyToken=c5687fc88969c44d" />
	</DbProviderFactories>
</system.data>

其中 Version 需要和上面复制到codesmith bin目录下的 MySql.Data.dll 版本一致,右键查看dll版本详情即可获取到

  1. 选择其他数据库,下载对应数据库的dll即可,重复以上操作即可

  2. 重新打开codesmith配置数据源即可


创建模板

首先创建一个简单的模板,用于生成数据库的实体类,选择 Template Explorer > My Templates 右键 new > Template(CSharp),写入一下内容

生成Model实体

创建 Model.cst 文件

<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" %>
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="tableName" %>
<%@ Property Name="FileNameFormat" Type="String" Category="Context" Default="{0}.cs" Description="FileName FormatStr" %>
<%@ Property Name="NameSpace" Type="System.String" Default="" Category="NameSpace" Description="NameSpace"  %>

<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>

using System;

namespace <%= string.IsNullOrEmpty(NameSpace)?"":NameSpace + ".Entity" %>
{
    // ToPascalCase 转化为驼峰命名
    public class <%= StringUtil.ToPascalCase(SourceTable.Name) %>
    {
    <% foreach (ColumnSchema column in this.SourceTable.Columns) {  %>
        public <%= CSharpAlias[column.SystemType.FullName] %> <%= StringUtil.ToPascalCase(column.Name) %> { get; set; }
    <% } %>
    }
}

选择文件,右键点击Exceute

执行Exceute

点击Generate

生成的模型类

生成Mapping

创建 Mapping.cst 文件

<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" %>
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context" Description="tableName" %>
<%@ Property Name="FileNameFormat" Type="String" Category="Context" Default="{0}Mapping.cs" Description="FileName FormatStr" %>
<%@ Property Name="NameSpace" Type="System.String" Default="" Category="NameSpace" Description="NameSpace"  %>

<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Management.Domain.Entity;

namespace <%= string.IsNullOrEmpty(NameSpace)?"":NameSpace + ".Mapping" %>
{
    public class <%= StringUtil.ToPascalCase(SourceTable.Name) + "Mapping" %> : IEntityTypeConfiguration< <%= StringUtil.ToPascalCase(SourceTable.Name)%> >
    {
        public void Configure(EntityTypeBuilder< <%= StringUtil.ToPascalCase(SourceTable.Name)%> > builder)
        {
                builder.ToTable("<%= SourceTable.Name%>");
            <% foreach (ColumnSchema column in this.SourceTable.Columns) {  %>
                builder.Property(e => e.<%= StringUtil.ToPascalCase(column.Name)%>).HasColumnName("<%= column.Name%>");
            <% } %>
        }
    }
}

选择文件,右键点击Exceute

执行Exceute

点击Generate

生成的模型类

制作用于批量调用的模板

首先按照文件夹创建子模板,然后外层新建 Generate.cst 的模板

目录结构

<%@ CodeTemplate Language="C#" TargetLanguage="C#"  Debug="True" Description="模板输出" %>

<%@ Property Name="NameSpace" Type="System.String" Default="" Category="NameSpace" Optional="True" Description="项目的命名空间"  %>

<%@ Property Name="SourceDatabase" Type="DatabaseSchema" Default="" Optional="True" Category="数据源" Description="选择数据库" %>
<%@ Property Name="SourceTables" Type="SchemaExplorer.TableSchemaCollection" Default="" Optional="True" Category="数据源" Description="选择数据表,默认空则为全部表" %>


<%@ Property Name="TemplateDirectory" Type="String" Editor="System.Windows.Forms.Design.FolderNameEditor" EditorBase="System.Drawing.Design.UITypeEditor" Optional="False" Category="模板" Description="--模板目录--"%>
<%@ Property Name="ExceptPrefix" Type="String" Default="" Optional="True" Category="模板" Description="排除前缀为__的" %>

<%@ Property Name="OutputDirectory" Type="String" Editor="System.Windows.Forms.Design.FolderNameEditor" EditorBase="System.Drawing.Design.UITypeEditor" Optional="False" Category="输出目录" Description="--输出目录--"%>

<%@ Assembly Name="System.Design" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>

<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Xml" %>
<%@ Import Namespace="System.Text" %>
<%@ Import Namespace="System.Windows.Forms.Design" %>
<%@ Import Namespace="System.Text.RegularExpressions" %>
<%@ Import Namespace="System.Collections.Specialized" %>
<%@ Import Namespace="System.Collections.Generic" %>

<script runat="template">
    //模板列表
    Dictionary<CodeTemplate, string> templates = new Dictionary<CodeTemplate, string>();

    //入口
    public void Generate()
    {
        GetTemplates(TemplateDirectory); //读取模板
  
        int tableIndex = 0;
        int templateIndex = 0;
        TableSchemaCollection tables = SourceTables != null && SourceTables.Count > 0 ? SourceTables : SourceDatabase.Tables; //若未手动选择数据表,则默认为全数据库
        foreach(TableSchema SourceTable in tables)
        {
            tableIndex++;

            Response.Write(string.Format("{0}.Table {1}",tableIndex, SourceTable.Name));
            Response.WriteLine();
            templateIndex = 0;
            foreach(var template in templates)
            {
                templateIndex++;
                Response.Write(string.Format("     {0}.template {1}",templateIndex, template.Key.CodeTemplateInfo.FileName));
                Response.WriteLine();
                template.Key.SetProperty("NameSpace",NameSpace);       //设置统一的命名空间
                template.Key.SetProperty("SourceTable",SourceTable);   //传入数据表的名称
                
                //读取模板的文件命名格式,生成文件名
                string FileName = template.Key.GetProperty("FileNameFormat") == null ? StringUtil.ToPascalCase(SourceTable.Name) : string.Format(template.Key.GetProperty("FileNameFormat").ToString(),StringUtil.ToPascalCase(SourceTable.Name));
                template.Key.RenderToFile(Path.Combine(template.Value, FileName), true); //按照模板的目录层级生成文件
            }
            Response.WriteLine();
        }
    }
    
    private void GetTemplates(string directoryStr)
    {
        DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryStr);
        GetTemplates(directoryInfo);
    }
    
    //递归方式读取所有满足要求的模板,记录模板的目录结构,生成代码文件时参照此时的目录结构
    private void GetTemplates(DirectoryInfo directoryInfo)
    {
        foreach (var file in directoryInfo.GetFiles())
        {
            if (!file.Extension.ToLower().Equals(".cst") || (!string.IsNullOrEmpty(ExceptPrefix) && file.Name.StartsWith(ExceptPrefix)))
            {
                continue;
            }
            
            CodeTemplateCompiler compiler = new CodeTemplateCompiler(file.FullName);
            compiler.Compile();   //编译子模板
            if (compiler.Errors.Count == 0)
            {
                templates.Add(compiler.CreateInstance(),directoryInfo.FullName.Replace(TemplateDirectory, OutputDirectory));
            }
            else
            {
                Response.WriteLine("编译模板" + file.FullName + "错误!");
            }
        }

        foreach (var directory in directoryInfo.GetDirectories())
        {
            GetTemplates(directory);
        }
    }
</script>

<% this.Generate();  %>

此模板文件的入口是Generate(),它首先会调用GetTemplates方法读取属性TemplateDirectory指定的目录下的所有模板,并记录各个模板所在的目录结构。然后判断属性SourceTables是否手动选择了数据表,如果没有则为所有表

选择文件,右键点击Exceute

执行Exceute

点击Generate

生成的信息

前往输出目录查看结果

输出结果

参数介绍

  • CodeTemplate 这个是模板中唯一必须的声明,包含一些模板特殊的属性,包含模板使用的语言、生成的语言和一些对于模板的描述

    • Language 开发模板使用的语言,如C#,VB.NET等。
    • TargetLanguage 编写的模板生成的哪种语言的代码,可以是任意一种语言
    • Debug 可以确定是否在模板中可以包含调试符号。如果将这个属性设置为 True,则可以使用 System.Diagnostics.Debugger.Break() 方法来设置断点
    • 其他参数详见 https://www.w3cschool.cn/codesmith/gvqk1hry.html
  • Property 定义一个可以从外部传入的变量

    • Name 模版使用的参数的名称
    • Type 参数类型可以是任何 .NET 有效的数据类型,例如简单的 String 类型或者是 CodeSmithSchemaExplorer.DatabaseSchema 类型。注意,类型必须是基类库的类型,例如用 String 或者 Int32 代替 stringint
    • Category 对变量的分组,用于在 CodeSmith Explorer 面板中显示
    • Default 设置默认值
    • Description 属性的描述
    • Optional 是否是必须
    • Editor 表明在属性面板中输入这个属性的值时使用何种 GUI(图形界面编辑器)编辑器,例如 System.Windows.Forms.Design.FolderNameEditor 即打开资源管理器
    • EditorBase 编辑器使用的基本类型,如果没有被说明,UITypeEditor 为默认编辑器
    • 其他参数详见 https://www.w3cschool.cn/codesmith/8rbh1hs4.html
  • Map 数据类型转换

    • Name 指定要在代码中使用的Map的参考名称
    • Src 映射文件的文件名引用。不需要添加扩展名
  • Assembly 引用其它文件或 .Net 类库,引入的dll路径在 codesmith安装目录的 AddIns

    • Name 指明所有需要引用的 Assembly 的名称
  • Import 引入 Assembly 所在的命名空间

    • Namespace 命名空间
    • 引入文件详情 https://www.w3cschool.cn/codesmith/bd181hsj.html