创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]

在《上篇》中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加、修改和删除操作的存储过程。但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板。如果我们提供一种基于多文件的代码生成方式,将会为编程人员带来极大的便利。借助于T4 ToolBox这个开源工具箱,多文件的SQL Generator的实现变得异常简单。[文中的例子可以从这里下载]

目录 一、多文件代码生成器会带来多大的便利? 二、创建自定义的Generator 三、ProcedureGenerator如何被使用?

一、多文件代码生成器会带来多大的便利?

我们先来直观的感受一下较之《上篇》提供的单一文件的代码生成器,基于多文件的代码生成解决方案会为开发人员带来多大的便利。 同样对于《上篇》创建的数据表T_PRODUCT,之前我们为了生成三个不同的存储过程,我们不得已需要创建3个不同的T4模板文件。实际上我们更需要的方式只需要创建一个T4模板,让我们的SQL Generator自动为我们生成3个包含相应存储过程的.sql附属文件,如左图所示(点击看大图)。

有的时候,基于单个数据表的存储过程生成方式我们依然觉得不方便。如果我们能够在T4模板文件中指定的数据表的列表,让我们的SQL Generator为列表的每一个数据表都生成CUD三个存储过程,这样的方式更加具有吸引力。如右图所示(点击看大图),一个订单模块包含两个具有主子关系的两张表(T_ORDER和T_ORDER_DETAIL),现在我们在一个T4模板中指定这两个表明,通过SQL Generator可以帮助我们生成6个包含存储过程的.sql附属文件。

甚至有的时候我们连数据表列表都无需指定,让SQL Generator为所有的表都生成相应的存储过程。我的例子中没有提供这样的功能,但是实现自来不会存在任何问题。

二、创建自定义的Generator

在《上篇》中我创建了一个抽象的ProcedureTemplate类,以及三个基于生成CUD存储过程的具体ProcedureTemplate:InsertProcedureTemplate、UpdateProcedureTemplate和DeleteProcedureTemplate。它们都将直接服务于我们今天将要提供的基于多文件的SQL Generator。

在《上篇》中,这四个Template分别定义在4个不同的TT文件中,3个具体的ProcedureTemplate通过<#@include>指令将抽象ProcedureTemplate模板文件包含过来。由于我们将要创建的T4模板将会使用到这四个类,如果我们用四个<#@include>指令将四个TT文件包含过来,由于T4引擎将会导致对ProcedureTemplate的4次包含,最好将会导致变异问题。个人觉得这应该算是T4引擎解析包含关系的一个局限性,为了解决这个问题我们不得不抽象的ProcedureTemplate和三个具体的ProcedureTemplate都合并成一个TT文件。

T4 ToolBox为类库中为了提供了一个抽象的T4Toolbox.Generator类用于实现多文件的代码生成。为此我们创建一个TT模板文件,定义了如下一个继承自该类的ProcedureGenerator。ProcedureGenerator的核心是通过属性Templates定义的类型为IEnumerable<ProcedureTemplate>的ProcedureTemplate列表,这个列表在存储过程中进行初始化。而对于ProcedureGenerator的构造函数,处理定义了一个表示数据库连接字符串的databaseName的参数外,并以数组参数的形式指定了生成的存储过程基于的数据表名的列表。

<#@ import namespace="System.Collections.Generic" #>
<#@ include file="ProcedureTemplate.tt" #>
<#@ include file="T4Toolbox.tt" #>
<#+
public class ProcedureGenerator : Generator
{
    public IEnumerable<ProcedureTemplate> Templates{get; private set;}

    public ProcedureGenerator(string databaseName, params string[] tableNames)
    {
        if(null == tableNames || tableNames.Length == 0)
        {
            throw new ArgumentNullException("tableNames");
        }    

        this.Templates = InitlizeTemplates(databaseName,tableNames);
    }

    private IEnumerable<ProcedureTemplate> InitlizeTemplates(string databaseName, string[] tableNames)
    {        
        foreach(string tableName in tableNames)
        {
            yield return new InsertProcedureTemplate(databaseName, tableName);
            yield return new UpdateProcedureTemplate(databaseName, tableName);
            yield return new DeleteProcedureTemplate(databaseName, tableName);
        }
    }

    protected override void RunCore()
    {
        foreach(ProcedureTemplate tempalte in this.Templates)
        {
            tempalte.RenderToFile(tempalte.GetProcedureName() + ".sql");
        }
    }
}
#>

真正的存储过程的T-SQL脚本实现在重写的RunCore中。由于具体的文本转化逻辑都定义在了ProcedureTemplate中了,所以在这里我们需要遍历的ProcedureTemplate集合中每一个Template对象,调用RenderToFile方法将相应的存储过程的脚本写入以存储过程命名同名的.sql文件中。

三、ProcedureGenerator如何被使用?

我们最后来看看我们创建的ProcedureGenerator最终如何被应用于具体的代码生成。其实很简单,我们只需要创建相应的模板文件,通过<#@include>将定义ProcedureGenerator类的TT文件包含近来,最后以代码语句调用块(<#StatementCode#>)的形式实力化该对象,并调用Run方法即可。在构造函数中指定数据库连接字符串的名称和数据表名的列表。下面是基于但表的T4模板。

<#@ template language="C#" hostSpecific="true" debug="true" #>
<#@ include file="Templates\ProcedureGenerator.tt" #>
<#
   new ProcedureGenerator("TestDb","T_PRODUCT").Run();
#>

下面是基于多表的T4模板:

<#@ template language="C#" hostSpecific="true" debug="true" #>
<#@ include file="Templates\ProcedureGenerator.tt" #>
<#
   new ProcedureGenerator("TestDb","T_ORDER","T_ORDER_DETAIL").Run();
#>

当你代码生成工作执行之后,会多出一个与TT文件同名的附属文件,你需要手工删除掉它。

从数据到代码——通过代码生成机制实现强类型编程[上篇] 从数据到代码——通过代码生成机制实现强类型编程[下篇] 从数据到代码——基于T4的代码生成方式 创建代码生成器可以很简单:如何通过T4模板生成代码?[上篇] 创建代码生成器可以很简单:如何通过T4模板生成代码?[下篇]

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黑泽君的专栏

day57_BOS项目_09

方式一:   第一步:获得activiti-eclipse-plugin.zip文件   第二步:将zip文件解压到eclipse的dropins目录中

10220
来自专栏JMCui

Apache solr(一).

概念:Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现。Apache...

38080
来自专栏数据库

详解Redis内部运作机制

纯手工打造每一篇开源资讯与技术干货,数十万程序员和Linuxer已经关注。 Linux技术交流QQ群:2659793(十二月最新!!) Redis数据库(Red...

22870
来自专栏木木玲

Netty 源码解析 ——— Netty 优雅关闭流程

51230
来自专栏北京马哥教育

Linux内存管理之mmap详解

作者:freeboy1015 来源:http://lib.csdn.net/article/linux/62126 一. mmap系统调用 1. mmap系统调...

85780
来自专栏熊训德的专栏

Hbase compaction 源码分析一:compaction 概况分析

本文档从框架的源码角度梳理了,hbase 在什么情况下会触发compaction,并通过官方文档说明出发minor 和major compaction的时间点。

55610
来自专栏逸鹏说道

在 ASP.NET MVC 中使用异步控制器

可以通过 AsyncController 类编写异步操作方法。 可以对长时间运行的、非 CPU 绑定的请求使用异步操作方法。 这样可避免在处理请求时阻塞 Web...

383110
来自专栏同步博客

浅谈PHP异常处理

  PHP中的异常的独特性,即PHP中的异常不同于主流语言C++、java中的异常。在Java中,异常是唯一的错误报告方式,而在PHP中却不是这样,而是把所有不...

16430
来自专栏码农阿宇

asp.net core轻松入门之MVC中Options读取配置文件

接上一篇中讲到利用Bind方法读取配置文件 ASP.NET Core轻松入门Bind读取配置文件到C#实例 那么在这篇文章中,我将在上一篇文章的基础上,利...

27440
来自专栏c#开发者

Msmq设计文档(赋源代码)

Msmq设计文档 文件状态: [√] 草稿 [ ] 正式发布 [ ] 正在修改 文件标识: ECI-MSMQ v01 当前版本: 0.5...

36380

扫码关注云+社区

领取腾讯云代金券