经过网友和昨天朋友的建议,今天我把NDOGenerator 代码生成器进行了重构,并在GUI上进行了增强,现在的NDOGenerator 生成器已经是一个通用的,使用方便的,易于扩展的代码生成器了。
在我的 也谈代码生成器 这篇文章中,我简要的介绍了代码生成器的7中设计方案,这篇文章我将以NDOGenerator为例详细介绍第五种设计方案的具体设计过程,希望和广大代码生成器爱好者在这个平台上相互分享一些设计思想!
如果要做一个通用的代码生成器(基于数据库驱动)必须具备的三要素:M V C
1:需要对数据库元数据进行建模(如,表,视图,列,视图列,存储过程,存储过程参数等)
2:一个良好的基于插件(命令模式)的控制器引擎
3:视图技术采用模板引擎
基于以上三要素来看NDOGenerator 的具体设计
1:NDOGenerator 数据库元数据模型层用的是NDO,NDO 组件内部已经内建了对数据库元数据模型以及元数据模型调用的API
2:NDOGenerator 是一个基于命令模式的控制器,接口非常简单

Instance 和GetInstance 是具有缓冲功能的工厂方法,该方法根据配置创建具体的命令,然后调用命令的Init 方法完成初始化工作。
抽象方法Init 是命令初始化方法,该方法会读区配置文件加载该命令的一些环境变量,如命名空间,输出目录,加载助手对象,模板文件等。
Generator 控制器运做机制分析:
NDOGenerator启动时Generator 控制器会读取配置文件,加载所有的插件(可能一个插件对应一个命令,也可能多个插件对应一个命令)配置信息。
当用户发出一个"Generate " 或"Preview" 请求时,Generator 控制器从上下文中取出或创建当前的插件对象既命令对象(一个插件对应一个命令类,一个命令类可以对应多个插件),然后命令对象进行一些处理,然后把处理结果和命令对象的配置信息一同存入视图引擎的上下文,最后命令对象调用视图引擎的解析方法把解析结果展现给用户。
NDOGenerator 提供了一个简单的命令类 SimpleGenerator,改类主要是完成普通的操作,像生成或批处理生成视图,存储过程,Dao,Biz,UI,实体类等,该命令类不支持一次生成N层框架的代码(注:可以通过一个技巧扩展助手类,在助手类上做文章),N层框架代码生成的命令类的创建可以参考 NDO - 快速入门 中所附代码生成中的NVGenerator类。SimpleGenerator 的类图

3:视图引擎部分,仍然用开元的Velocity引擎,我把Velocity引擎进行了简单的包装-NVelocityShell,接口如下:

关于Velocity 的VTL 语言介绍请看官方网站 http://jakarta.apache.org/velocity/docs/user-guide.html
NDOGenerator 对视图部分也做了很多扩张,用户可以很方便的开发VTL 的“标签” - helper,该“标签”的配置非常简单,如javaBean 的输出”标签“ helper="NDO.Tools.JavaHelper,NDO.Tools"。
NDOGenerator 代码生成器的配置如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="generator" type="NDO.Configuration.ProviderConfigurationHandler, NDO.Common"/>
</configSections>
<appSettings>
<add key="ConnectionString" value="Data Source=localhost; Database=Northwind; Integrated Security=true;"/>
<add key="JetConnectionString" value="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Northwind.mdb;"/>
<add key="OracleConn" value="Provider=MSDAORA.1;Data Source=***;User ID=***;Password=***;"/>
<add key= "SqlOledb" value="Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=Northwind;User ID=sa;Password=;"/>
<add key="Oracle" value="Data Source=***;User ID=***;Password=***;"/>
</appSettings>
<generator defaultProvider="Test.Driver">
<providers>
<clear/>
<!-- outLanguage ="CSHARP,VBNET,ASPX,HTML,JAVA,JAVASCRIPT,SQL,XML,CPP"-->
<add
name="simple"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.CSHelper,NDO.Tools"
outLanguage ="CSHARP"
templateName ="Model.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".cs"
ns="NDOTest.Model"/>
<add
name="javaModel"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.JavaHelper,NDO.Tools"
outLanguage ="Java"
templateName ="Java.Model.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".java"
ns="NDOTest.Model"/>
<add
name="javaModel2"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.JavaHelper,NDO.Tools"
outLanguage ="Java"
templateName ="Java.Model2.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".java"
ns="NDOTest.Model"/>
<add
name="ndoEntity"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.NDOHelper,NDO.Tools"
outLanguage ="CSHARP"
templateName ="NDO.Entity.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".cs"
ns="NDOTest.Model"/>
<add
name="ndoActiveRecord"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.NDOHelper,NDO.Tools"
outLanguage ="CSHARP"
templateName ="NDO.ActiveRecord.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".cs"
isInMeta ="0"
ns="NDOTest.Model"/>
<add
name="ndoActiveRecord2"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.NDOHelper,NDO.Tools"
outLanguage ="CSHARP"
templateName ="NDO.ActiveRecord2.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".cs"
isInMeta ="0"
ns="NDOTest.Model"/>
<add
name="Test.Driver"
type="NDO.Tools.SimpleGenerator,NDO.Tools"
helper="NDO.Tools.NDOHelper,NDO.Tools"
outLanguage ="CSHARP"
templateName ="Test.Driver.vm"
outDir="D:\temp\NW\Model"
outExtensionName=".cs"
isInMeta ="0"
ns="NDOTest.Model"/>
</providers>
</generator>
</configuration>

在SimpleGenerator 命令对象中向 Velocity 引擎注入了一个 driver 对象,该驱动器对象实现了数据库元数据提供者接口,所以在任何一个模板文件中都可以自由自在的操作整个数据库的元数据。比如下面的 模板文件代码:

----------------------- Driver Info -------------------------------
Current ConnectionString :$driver.ConnectionString
Driver.NamedPrefix :$driver.NamedPrefix
Dialect.OpenQuote :$driver.Dialect.OpenQuote
Dialect.CloseQuote :$driver.Dialect.CloseQuote
#if ($driver.SupportsIdentitySelectInInsert)
Dialect.IdentitySelectString :$driver.Dialect.IdentitySelectString
#end
----------------------- End Driver Info ----------------------------




----------------------- All Tables -------------------------------
#set($sc = $driver.GetTableList())
#foreach($s in $sc)
$s
#set($cols = $driver.GetTableSchema($s).Columns)
#foreach($col in $cols)
$col.Name $col.ColumnType $col.DbType $col.Length
#end
#end
----------------------- End Tables -------------------------------




----------------------- All Views -------------------------------
#set($sc = $driver.GetViewList())
#foreach($s in $sc)
$s
#set($cols = $driver.GetViewSchema($s).Columns)
#foreach($col in $cols)
$col.Name $col.ColumnType $col.DbType $col.Length
#end
#end
----------------------- End Views -------------------------------




----------------------- All Procudures --------------------------
#set ($pss = $driver.GetSPList())
#foreach($ps in $pss)
$ps
#set($parameters = $driver.GetSPSchema($ps).Parameters)
#foreach($parameter in $parameters)
$parameter.Name $parameter.DbType $parameter.NativeType $parameter.Direction $parameter.Size
#end
#end
----------------------- End Procudures---------------------------
.NET 实体类的模板文件
using System;

namespace $ns


{
#foreach($tb in $tbs)
public class $tb.Name

{

Field#region Field
#foreach($col in $tb.Columns)
private $helper.DbTypeToCS($col.DbType) $helper.LowerNameForFirstCharacter($col.Name);
#end
#endregion

Property#region Property
#foreach($col in $tb.Columns)
public $helper.DbTypeToCS($col.DbType) $helper.UpperNameForFirstCharacter($col.Name)

{

get
{ return $helper.DbTypeToCS($col.DbType) $helper.LowerNameForFirstCharacter($col.Name); }

set
{ $helper.DbTypeToCS($col.DbType) $helper.LowerNameForFirstCharacter($col.Name) = value; }
}
#end
#endregion
}
#end
}
.NET 实体类的模板文件2 - 基于扩展"标签" Helper
using System;

namespace $ns


{
#foreach($tb in $tbs)
public class $tb.Name

{

Field#region Field
#foreach($col in $tb.Columns)
$helper.Field($col)
#end
#endregion

Property#region Property
#foreach($col in $tb.Columns)

/**//// <summary>
/// $helper.PropComment($col)
/// </summary>
$helper.Property($col)
#end
#endregion
}
#end
}

下面是几个GUI截图:



日志信息:

最后附上代码生成器原代码和新版本的NDO组件(增强了对视图的支持,以及非活动记录的单表ORM的支持)