信息化基础建设 改善代码生成
数据库字段映射
当看到一个实体的属性,是否可以马上判断出它对应的数据库字段?
_languageTranslation.KeyText, 比如看到这个KeyText的属性
通常的做法是,去查找实体与数据库字段绑定的代码,以检查这个属性关联的数据库字段,代码可能是这样
public LanguageTranslationEntity ReaderBind(IDataReader dataReader)
{
LanguageTranslationEntity model=new LanguageTranslationEntity ();
object ojb;
model. KeyText =dataReader[“KEY_TEXT"].ToString();
model.Description=dataReader["Description"].ToString();
}
看到KeyText属性对应的数据库字段是KEY_TEXT. 但是,你需要用F12,转到定义,来帮忙查看这个ReaderBind方法,如果加上了接口与实现,还需要额外的搜索才能确定字段来源。
改进的一种办法,是利用C#的Xml注释与智能感知,如下图
在生成的实体文件中,加上Xml注释,当鼠标指向一个属性时,可得到如下的效果
从这个图片中可以看出KeyText映射到表LanguageTransaction的KeyText字段,非常清晰。
适当的修改一下生成实体的代码生成器,给字段加上一段类似于下面的Xml注释,可增强代码的可读性。
<remarks>Mapped on table field: "LanguageTranslation"."KeyText"<br/>
这是我对代码生成器的第一个Enhancement。
一组实体文件
实体名是LanguageTranslationEntity,我们通常会生成一个对应的LanguageTranslationEntity.cs文件。
如果要给LanguageTranslationEntity.cs做一下修改,可能加入一些方法,F5,代码运行正常。
如果有一天需要给LanguageTranslationEntity实体对应的数据库表增加一个字段,比如修改进间RevisedDate,这时,需要重新打开代码生成器,再生成一次代码,糟糕,原来写的代码丢失了。因为我们的代码生成器,只是简单的把文件写入到磁盘上去,如果存在原文件,默认的办法是覆盖原来的文件。
这个问题有2个办法解决,改进代码生成器,或是改进生成的文件。
改进代码生成器,把我们写的代码,放到代码文件中指定的区域中,
比如,给生成的文件加上一些标签,在生成代码的时候,如果原来的文件是存在的,需要先提取这些标签中的字符串值,即程序员加入的自定义代码,然后把这些值插入到新生成的代码文件中,比如下面的标签例子
// CODE_REGION_START
自定义的代码逻辑放在这里
// CODE_REGION_END
第二种办法是,利用partial特性,生成2个文件,一个LanguageTranslationEntity.cs,另一个LanguageTranslationEntity.Logic.cs, 第二个文件,只生成一次,以后不重新生成,里面的内容全部放我们的自定义逻辑代码。
表名与属性命名
表名是Language,生成的实体名是LanguageEntity,在表名后面加Entity.
如果喜欢把表名写成tblLanguage, 生成实体为LanguageEntity时,需要去掉tbl前缀。
如果把表名写成LANGUAGE,要生成LanguageEntity,则需要转化一下大小写。把首字母大写,其余的小写。
如果表名是ProductionControl,要生成ProductionControlEntity, 也是直接加Entity
如果表名是DeptGroup,默认生成的实体为DeptGroupEntity, 为了可读性,可能需要转化一下,把Dept转成Department,生成的实体名为DepartmentGroupEntity.
如果表名是DeptGrp,生成的DeptGrpEntity可读性就越来越差了。
这时,转化Dept为Department,Grp为Group是必须的。
要改善复合单词的表名对应的实体名,需要建立一个缩写字典表,和一个前缀表,在生成表名对应的实体名时,做替换处理,以增强可读性。
这里还有一个问题,如何判断出DeptGroup是2个单词,一个简写和一个单词,
而DeptGrp是2个单词的缩写,如果缩写恰好又是一个正确英语单词呢?
还没有找到正确的区分单词的办法,折衷的方法是表名写成Dept_Grp,主动用下划线分开表名中的单词,在生成实体名时,只需要简单的Regex.Split就可以了。
字段的命名,以及生成的属性名称,也可参考表名的规则,是同样的原理。
接口与实现
表名是Language,生成实体LanguageEntity, 实体文件名也叫LanguageEntity.cs
如果同时加上接口与实现,如何命名文件名? 有以下几种方案参考
方案1 接口文件名 ILanguage.cs , 实现文件名LanguageImplementation.cs
方案2 接口文件名 ILanguageDAL.cs , 实现文件名LanguageDAL.cs
方案3 接口文件名 ILanguageService.cs , 实现文件名LanguageService.cs
方案4 接口文件名 ILanguageManager.cs , 实现文件名LanguageManager.cs
用CodeSmith写模板文件,要改换上面提到的几种写法,非常容易
如果需要为生成的接口支持WCF技术, 生成接口时,要主动接口添加ServiceContract特性,给方法添加OperationContract特性。如下面的例子
[ServiceContract]
public interface IWCFExample
{
[OperationContract]
IEntity2 GetCustomer(string customerID);
[OperationContract]
IEntityCollection2 GetCustomers();
}
如果生成的接口要支持Web Services, 如下面的例子所示,加上WebMethod标签。
[WebService]
public class CustomerService : System.Web.Services.WebService
{
[WebMethod]
public IEntity2 GetCustomer(string customerID)
{
……….
}
}
好了,这些独立的部分考虑到了,用CodeSmith做好各个模板,生成代码起来很轻松
但是,CodeSmith有个问题,就是一次只能生成一段代码,如果我需要为数据库Northwind中所有表(或是指定的表)生成接口和实现文件呢,dirty的办法可以多次的在CodeSmith的输入的表名,生成文件,一个表一个个的做代码生成,再加上同时生成Interface和Manager,10个表*2,工作量是20次,如果是100个表,工作量是100*2, 200次运动。
CodeSmith中有一个功能,就是可以直接调用它的API来运行代码生成,传入模板文件和必要的参数,调用代码就可以生成代码,而不必要运行CodeSmith软件本身。
顺着这个思路,我制作出如下的实用工具
这个工具的目的,就是克服CodeSmith一次不能批量工作的局限。如上图所示,打开一个服务器,选择数据库,左边自动列出它的表名,双击表名或是拖动表名到中间的Listbox中,这里面的表,是要进行代码生成的表,再看右边的选项区,Template列出了指定文件下的所有模板,也就是要进行代码生成的模板,Parameter区域列出选定模板的参数,我使用=号把值填写到后面,下面的TargetFolder是生成的文件放置的目录。
点击OK,一下次为这些表GBCURR Catalog DataSets Event History ModelDrill ModelPerspective Notifications
生成了所有的Template中列出的模板的文件。
表数量是8个,模板数据是7个,点击OK后,生成了7*8,56个文件,效率高。
事实上,在升级.Net Remoting为WCF时,我就是使用这个技巧,一下将所有的接口文件实现成WCF的标准接口。
Text to String
在做报表或是数据库操作时,通常需要在查询分析器中写好SQL,如下
SELECT
ItemID, Path, Name, ParentID, Type,
Content,
Intermediate, SnapshotDataID, LinkSourceID,
FROM dbo.Catalog
然后运用SQL拼接技术,写成下面的格式
string sql=" SELECT ItemID, Path, Name, ParentID, Type,"+
" Content, Intermediate, SnapshotDataID, LinkSourceID, "+
" FROM dbo.Catalog";
这个转换的步骤,容易出错,也枯燥。如果有大量的SQL要转化成C#,工作量会相当大。
由于掌握了代码生成技术 ,来看一下这个小程序
把SQL拷贝到上面的窗格中,选择要生成的代码方法(C#或是VB),点Generate Code,
下面的窗格就写好我们需要的代码。
这个小技巧可以省很多拼凑SQL的时间,而你需要付出的,就是写一个不到10行的C#程序。
不可否认,SQL拼凑,在制作复杂的报表中还是相当有用的,复杂的报表要从大量的表中取数,运算,然后生成新的数据集,传送到报表中。用SQL拼凑做数据访问层,显然不是明智的决定,我以为。