使用C#打造代码生成器
作者:陈缘
联系:luandao2000@gmail.com
博客:http://blog.csdn.net/luandao2000
主页:http://www.spbase.com
使用VS.NET开发WEB应用的同志都会接触到.NET平台上两个非常经典的例子:Duwamish和Petshop,两个例子都是微软为了演示n层架构而开发的示例,因为是微软内部不同的小组所开发所以风格差异较大。孰优孰劣我不敢妄作评价,但在我目前所作的项目中全部是采用Duwamish的结构来设计整个系统的,就目前的情况来开,效果还是比较好的。
Duwamish的数据表示层完成数据表到业务实体的映射,采用有类型的DataSet来描述业务实体的结构,这部分代码依赖于构成业务实体的数据表的结构,大家从Duwamish的实现也可以看到这点。表示层的代码格式简单,没有任何逻辑,基本上只是描述了结构而已,所以对于这部分代码可以采用代码生成器来完成。
下来看看数据访问层,基本上类似,数据访问层完成实际的数据读写工作,但大部分简单的增、删、改、查的代码还是比较简单,格式比较固定:先构造存储过程的入口参数,构造相应操作的Command对象,调用适配器完成操作,如果没有特殊的要求,这部分代码的完成也可以自动生成。
好了分析完成代码的格式后,我们来考虑一下我们需要一个什么样的代码生成器呢?首先,能根据构成业务实体的数据表格式生成表示层和数据访问层的代码,其次必须要能支持相应的配置。这样基本上可以减少很多重复性的工作。对采用Duwamish架构的系统来说,可以生成对的业务实体的一些简单操作,最后最好能自动生成增删改的存储过程,这样基本上就完成了一个数据库系统的雏形。
实际上大部分的代码生成器也就是完成这些功能,根据你的选择生成一个模板性质的代码,这样很多工作就不用再重复作,后期维护时也会方便很多。
好了废话就不再说了,开始动手:
1、先设计各个层的模板文件和替换的参数
2、根据选择的数据表生成动态代码部分
3、生成相应的代码文件和存储过程
先说各个层的模板文件,其实参考一下表示层的代码可以很明显看出来:有类型DataSet所描述的数据实体基本只是数据表在程序中的映射而已,当然我们必须考虑Master-Detail表的关系,大部分的业务实体不是单独一个数据表能描述清楚的。这里给出一个数据访问层模板文件的示例:
namespace ServiceCard.DataAccess
{
using System;
using System.ComponentModel;
using System.Data;
using System.Data.OracleClient;
using System.Configuration;
using ServiceCard.Common;
/// <summary>
/// Card info
/// </summary>
public class $classname$ : AccessBase
{
//
// DataSetCommand object
//
//private OracleDataAdapter dsCommand;
//
// Stored procedure parameter names
//
private OracleCommand loadCommand;
private OracleCommand insertCommand;
private OracleCommand updateCommand;
private OracleCommand deleteCommand;
//private string ServiceCardConnectionString=ConfigurationSettings.AppSettings["ServiceCardConnectionString"];
private const string PROCESS_PARM = "GH_PACKAGE.LOAD_WORKSHEET";
private const string CURSOR_PARM = "ret_cur";
private const string VALUE_PARM = "in_value";
$parm$
public $classname$()
{
//
// TODO: add your code
//
dsCommand = new OracleDataAdapter();
dsCommand.TableMappings.Add("$tablename$", $dataname$.$constname$);
}
public $classname$(OracleConnection base_connect,OracleTransaction base_mytrans)
{
//
// TODO: add your code
//
dsCommand = new OracleDataAdapter();
dsCommand.TableMappings.Add("$tablename$", $dataname$.$constname$);
connect=base_connect;
mytrans=base_mytrans;
bTrans=true;
}
/// <summary>
/// Dispose of this object's resources.
/// </summary>
//public void Dispose()
//{
// Dispose(true);
// GC.SuppressFinalize(true); // as a service to those who might inherit from us
//}
/// <summary>
/// Free the instance variables of this object.
/// </summary>
///protected override void Dispose(bool disposing)
///{
/// if (! disposing)
/// return; // we're being collected, so let the GC take care of this object
///
/// if (dsCommand != null)
/// {
/// if(dsCommand.SelectCommand != null)
/// {
/// if( dsCommand.SelectCommand.Connection != null )
/// {
/// dsCommand.SelectCommand.Connection.Dispose();
/// }
/// dsCommand.SelectCommand.Dispose();
/// }
/// dsCommand.Dispose();
/// dsCommand = null;
/// }
///}
//----------------------------------------------------------------
// Sub BuildLoadCommands:
// Initialize the parameterized Load command for the DataAdapter
//----------------------------------------------------------------
private OracleCommand GetLoadCommand()
{
if ( loadCommand == null )
{
//
// Construct the command since we don't have it already
//
loadCommand = new OracleCommand(PROCESS_PARM,new OracleConnection (ServiceCardConnectionString));
loadCommand.CommandType = CommandType.StoredProcedure;
loadCommand.Parameters.Add(new OracleParameter(CURSOR_PARM, OracleType.Cursor));
loadCommand.Parameters[CURSOR_PARM].Direction = ParameterDirection.Output;
loadCommand.Parameters.Add(new OracleParameter(VALUE_PARM, OracleType.VarChar));
}
return loadCommand;
}
private OracleCommand GetDeleteCommand()
{
if ( deleteCommand == null )
{
//
// Construct the command since we don't have it already
//
if(bTrans)
deleteCommand = new OracleCommand("$deletecommand$",connect);
else
deleteCommand = new OracleCommand("$deletecommand$",new OracleConnection (ServiceCardConnectionString));
deleteCommand.CommandType = CommandType.StoredProcedure;
deleteCommand.Transaction = mytrans;
OracleParameterCollection OracleParams = deleteCommand.Parameters;
$deleteconest$
}
return deleteCommand;
}
private OracleCommand GetUpdateCommand()
{
if ( updateCommand == null )
{
//
// Construct the command since we don't have it already
//
if(bTrans)
updateCommand = new OracleCommand("$updatecommand$",connect);
else
updateCommand = new OracleCommand("$updatecommand$",new OracleConnection (ServiceCardConnectionString));
updateCommand.CommandType = CommandType.StoredProcedure;
updateCommand.Transaction = mytrans;
OracleParameterCollection OracleParams = updateCommand.Parameters;
$updateparmadd$
//
// Define the parameter mappings from the data table in the
// dataset.
//
$updateprarmequ$
}
return updateCommand;
}
//----------------------------------------------------------------
// Sub BuildInsertCommands:
// Initialize the parameterized Load command for the DataAdapter
//----------------------------------------------------------------
private OracleCommand GetInsertCommand()
{
if ( insertCommand == null )
{
//
// Construct the command since we don't have it already
//
if(bTrans)
insertCommand = new OracleCommand("$insertcommand$",connect);
else
insertCommand = new OracleCommand("$insertcommand$",new OracleConnection (ServiceCardConnectionString));
insertCommand.CommandType = CommandType.StoredProcedure;
insertCommand.Transaction = mytrans;
OracleParameterCollection OracleParams = insertCommand.Parameters;
$updateparmadd$
//
// Define the parameter mappings from the data table in the
// dataset.
//
$updateprarmequ$
}
return insertCommand;
}
/// <summary>
/// Inserts a new card into the database.
/// <param name="card">A CardData containing detailed card information.</param>
/// <retvalue>Success or failure of the database insert.</retvalue>
/// </summary>
public bool Insert$workobject$($dataname$ $paraname$)
{
if ( dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}
dsCommand.InsertCommand = GetInsertCommand();
dsCommand.Update($paraname$, $dataname$.$constname$);
//
// Check for table errors to see if the update failed.
//
if ( $paraname$.HasErrors )
{
$paraname$.Tables[$dataname$.$constname$].GetErrors()[0].ClearErrors();
return false;
}
else
{
$paraname$.AcceptChanges();
return true;
}
}
/// <summary>
/// Inserts a new card into the database.
/// <param name="card">A CardData containing detailed card information.</param>
/// <retvalue>Success or failure of the database insert.</retvalue>
/// </summary>
public $dataname$ Load$workobject$(string SN)
{
if ( dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}
$dataname$ $paraname$ = new $dataname$();
dsCommand.SelectCommand=GetLoadCommand();
dsCommand.SelectCommand.Parameters[VALUE_PARM].Value = SN;
dsCommand.Fill($paraname$,$dataname$.$constname$);
//
// Check post conditions
//
return $paraname$;
}
public bool Update$workobject$($dataname$ $paraname$)
{
if ( dsCommand == null )
{
throw new System.ObjectDisposedException( GetType().FullName );
}
dsCommand.UpdateCommand=GetUpdateCommand();
dsCommand.Update($paraname$, $dataname$.$constname$);
//
// Check for table errors to see if the update failed.
//
if ( $paraname$.HasErrors )
{
$paraname$.Tables[$dataname$.$constname$].GetErrors()[0].ClearErrors();
return false;
}
else
{
$paraname$.AcceptChanges();
return true;
}
}
public bool Delete$workobject$(string thekey)
{
bool result=false;
OracleCommand deleteCommand=new OracleCommand("$deletecommand$",new OracleConnection (ServiceCardConnectionString));
deleteCommand.Connection.Open();
deleteCommand.CommandType=CommandType.StoredProcedure;
deleteCommand.Parameters.Add(new OracleParameter(CHARGE_CODE_PARM, OracleType.VarChar, 10));
deleteCommand.Parameters[CHARGE_CODE_PARM].Value=charge_code;
try
{
deleteCommand.ExecuteNonQuery();
result=true;
}
catch(Exception ex)
{
result=false;
throw new Exception(ex.Message);
}
finally
{
deleteCommand.Connection.Close();
deleteCommand.Connection.Dispose();
deleteCommand.Parameters.Clear();
deleteCommand.Dispose();
}
return result;
}
}
}
模板文件很简单:和数据表格式相关的地方采用标签来代替,程序中根据表的结构、关联生成这部分代码并且模板文件中的标签设置生成最终代码。
替换部分的代码如下:
private StringBuilder ReplaceValue(StringBuilder content)
{
constList.Clear();
valueList.Clear();
InitConstList();
InitValueList();
int index=0;
foreach (string item in constList)
{
content.Replace(item,valueList[index].ToString());
index+=1;
}
return content;
}
private void InitConstList()
{
string str="";
//1
str="$classname$";
constList.Add(str);
//2
str="$parm$";
constList.Add(str);
//3
str="$tablename$";
constList.Add(str);
//4
str="$dataname$";
constList.Add(str);
//5
str="$constname$";
constList.Add(str);
//6
str="$loadcommand$";
constList.Add(str);
//7
str="$deletecommand$";
constList.Add(str);
//8
str="$deleteconest$";
constList.Add(str);
//9
str="$insertcommand$";
constList.Add(str);
//10
str="$updatecommand$";
constList.Add(str);
//11
str="$updateparmadd$";
constList.Add(str);
//12
str="$updateprarmequ$";
constList.Add(str);
//13
str="$paraname$";
constList.Add(str);
//14
str="$systemclassname$";
constList.Add(str);
//15
str="$workobject$";
constList.Add(str);
}
private void InitValueList()
{
string str="";
//$classname$ =类名1 数据访问层类名称
str=DataName.Text+"s";//ClassName.Text;
valueList.Add(str);
//$parm$ =声明存储过程需要的参数2
str=t1;
valueList.Add(str);
//$tablename$ =数据库中的表名3
str=strTablename;
valueList.Add(str);
//$dataname$ =表示层 该数据对象的类名4
str=DataName.Text+"Data";
valueList.Add(str);
//$constname$ =表示层 该数据对象表名的常量名5
str=strTablename+"_TABLE";
valueList.Add(str);
//$loadcommand$ =加载存储过程名6
str="LOAD_"+strTablename;
valueList.Add(str);
//$deletecommand$ =删除存储过程名7
str="DELETE_"+strTablename;
valueList.Add(str);
//$deleteconest$ =删除参数说明8
str="//you shuld add delete parm ";
valueList.Add(str);
//$insertcommand$ =新增存戳过程名9
str="INSERT_"+strTablename;;
valueList.Add(str);
//$updatecommand$ =更新存储过程名10
str="UPDATE_"+strTablename;;
valueList.Add(str);
//$updateparmadd$ =增加更新需要的参数11
str=t2;
valueList.Add(str);
//$updateprarmequ$ =制定这些参数的源列12
str=t3;
valueList.Add(str);
//$paraname$ =从逻辑层传输的业务对象 名称13
str=DataName.Text.ToLower();;
valueList.Add(str);
//增加业务逻辑层的处理
str=DataName.Text+"System";
valueList.Add(str);
//增加对象
str=DataName.Text;
valueList.Add(str);
}
大体思想已经描述清楚 需要源码请来信索取