最近开始新的项目,在数据库设计时使用了excel对各表结构及字段属性进行详细描述,项目内使用ORM进行数据库通讯,ORM以CodeFirst模式建立数据库,所以首先要在项目内建立各表的实体类。建立实体类时会依据excel表建立类,依据表内字段建立类属性,依据表内字段属性建立类内属性特性,所以决定使用T4模板读取excel表自动生成实体类。下面以示例形式进行说明。
一.excel表数据结构。
T4模板编写与excel表数据结构是紧密联系的,示例excel表数据结构如下图所示:
1.excel内每张工作表内设计一个数据库表,第一张表为设计说明及对应的T4模板版本说明,T4模板读取excel时忽略第一张表。
2.表内内部名称为实体类名称,字段名称为类属性,依据表内数据类型建立类属性类型,依据表内长度、属性建立类属性特性。
二.T4模板建立实体类流程,逻辑非常简单不上图了
1.T4模板打开一个excel表文件
2.从excel第二张工作表开始,依据内部名称建立实体类
3.遍历字段名称建立类属性,同时读取数据类型、长度、属性建立类属性类型及特性
4.字段读取为空时读取下一张excel工作表。
三.编写T4
1.环境
编程环境:VS2015
编程语言:C#
2.T4组织结构
上面的excel表是非常简单的,为了方便升级将T4生成实体类部分与T4读取excel文件部分进行分离,将生成实体类部分单独建立在一个文件内并指定版本号,对应结构的excel表建立相同的版本号,这样在项目内建立T4后只需将excel表对应版本的实体类部分T4包含进来即可。项目内T4模板名称后缀为.tt,实体类生成部分文件名称后缀为.t4。
3.T4实体类输出说明
默认每个T4只生成一个文本输出,由于每个excel文件包含多个表,而每张工作表会生成一个实体类,虽然所有的类都放在同一个.cs文件内也可以,但是这样在实际编写代码时总是不顺手,所以我们的T4模板需要同时输出多个文件,关于一个T4模板生成多个输出可以查看这里。
4.参考上述excel数据结构编写T4模板实体类生成部分
<#@ assembly name="System.Core" #> <#@ assembly name="Microsoft.CSharp" #> <#@ import namespace="Microsoft.CSharp.RuntimeBinder" #> <#@ import namespace="System.Text.RegularExpressions" #> <#@ assembly name="D:\C#\ExcelWork\ExcelLibray\bin\Debug\ExcelLibray.dll" #> <#@ import namespace="ExcelLibray" #> <#@ include file="D:\T4\MultipleOutPut.t4"#> <#+ int CreateEntity(string namespaceName,string excelModelFile,string outputPath="") { if(string.IsNullOrEmpty(excelModelFile)) return 0; var manager = Manager.Create(Host, GenerationEnvironment); string tmpNamespaceName=namespaceName; string tmpExcelFile=excelModelFile; Excel tmpExcelModels=new Excel(tmpExcelFile); int tmpModelCount=tmpExcelModels.SheetsCount-1; int rtc=0; if(tmpModelCount>1) { for(int tmpSheetIndex=2;tmpSheetIndex<tmpModelCount+2;tmpSheetIndex++) { tmpExcelModels.SetCurrentSheet(tmpSheetIndex); string tmpEntityName=tmpExcelModels.GetCell(2,3).ToString(); if(string.IsNullOrEmpty(tmpEntityName)) continue; string tmpOutFileName =tmpEntityName+".cs"; if(!string.IsNullOrEmpty(outputPath)) tmpOutFileName=outputPath+"\\"+tmpOutFileName; manager.StartNewFile(tmpOutFileName); #> using System; using System.ComponentModel.DataAnnotations; namespace <#= tmpNamespaceName #> { public class <#= tmpEntityName #> { <#+ int tmpColumnIndex=2; while(true) { string tmpColumnName=tmpExcelModels.GetCell(tmpColumnIndex,6); //获取列名 if(string.IsNullOrEmpty(tmpColumnName)) break; string tmpColumnRemark=tmpExcelModels.GetCell(tmpColumnIndex,5); //获取列注释 if(!string.IsNullOrEmpty(tmpColumnRemark)) { #> /// <summary> /// <#= tmpColumnRemark #> /// </summary> <#+ } string tmpKeyType=tmpExcelModels.GetCell(tmpColumnIndex,12); //获取列Key属性 if(!string.IsNullOrEmpty(tmpKeyType)) { if(tmpKeyType.Equals("主键")) { #> [Key] <#+ } } dynamic tmpColumnLength=tmpExcelModels.GetCell(tmpColumnIndex,9); //获取长度属性 if(tmpColumnLength!=null) { tmpColumnLength=tmpColumnLength.ToString().ToLower().Trim(); string tmpLen=Regex.Match(tmpColumnLength,"\\d+").Value; if(tmpColumnLength.StartsWith("max")) { #> [MaxLength(<#= tmpLen #>)] <#+ } else if(tmpColumnLength.StartsWith("min")) { #> [MinLength(<#= tmpLen #>)] <#+ } else if(!string.IsNullOrEmpty(tmpColumnLength)) { #> [StringLength(<#= tmpColumnLength #>)] <#+ } } string tmpColumnType=tmpExcelModels.GetCell(tmpColumnIndex,7); //获取数据类型 #> public <#= tmpColumnType #> <#= tmpColumnName #> { get; set; } <#+ tmpColumnIndex++; } #> } } <#+ manager.EndBlock(); } rtc++; } manager.Process(true); return rtc; } #>
该文件内只定义了一个方法 int CreateEntity(string namespaceName,string excelModelFile,string outputPath="")供项目内.tt文件调用,参数namespaceName指定生成实体类的命名空间,excelModelFile时excel设计文件全名,outputPath是生成的cs文件存放路径,缺省时放在项目内.tt文件当前目录下。另外文件包含的“D:\C#\ExcelWork\ExcelLibray\bin\Debug\ExcelLibray.dll”是一个excel表处理类库,主要用来读取excel文件数据,截取主要使用的部分代码如下:
public class Excel:IDisposable { private Application _excelApp; private Workbooks _workBooks; private _Workbook _workBook; private Sheets _sheets; private _Worksheet _currentSheet; /// <summary> /// 工作表集合 /// </summary> public Sheets Sheets { get { return _sheets; } } /// <summary> /// 工作表数量 /// </summary> public int SheetsCount { get { if (_sheets != null) return _sheets.Count; else return 0; } } /// <summary> /// 打开现有的excel文件 /// </summary> /// <param name="path"></param> public Excel(string path) { this._excelApp = new Application(); this._workBooks = _excelApp.Workbooks; this._workBook = _workBooks.Add(path); this._sheets = _workBook.Sheets; SetCurrentSheet(1); } /// <summary> /// 设置当前操作的工作表 /// </summary> /// <param name="index">工作表序列</param> /// <returns>当前工作表序列</returns> public int SetCurrentSheet(int index) { int rtc = 0; if (this._sheets.Count >= index & index > 0) { this._currentSheet = this._sheets[index]; rtc = index; } else rtc = -1; return rtc; } /// <summary> /// 获取单元格内容 /// </summary> /// <param name="columnIndex"></param> /// <param name="rowIndex"></param> /// <returns>单元格内容</returns> public dynamic GetCell(int columnIndex, int rowIndex) { dynamic rtc=default(dynamic); Range tmpCell = this._currentSheet.Cells[columnIndex][rowIndex]; if (tmpCell != null) rtc = tmpCell.Value; return rtc; } }
5.项目内T4模板如下
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ include file="D:\T4\EntityTemplate\EntityTemplate.t4" #> <# string tmpNamespaceName="BLL.Entity"; string tmpExcelFile=@"F:\项目\...\01 用户.xlsx"; int tmpEntitiesCount=CreateEntity(tmpNamespaceName,tmpExcelFile,""); #>
其中 tmpExcelFile指定的是excel数据库设计文件,建立完成后保存即可生成所有实体类代码,生成的用户实体类代码如下:
using System; using System.ComponentModel.DataAnnotations; namespace BLL.Entity { public class User { /// <summary> /// 序号 /// </summary> public int ID { get; set; } /// <summary> /// 账号 /// </summary> [MaxLength(64)] public string Account { get; set; } /// <summary> /// 密码 /// </summary> [StringLength(128)] public string Password { get; set; } /// <summary> /// 昵称 /// </summary> [MaxLength(64)] public string Nick { get; set; } /// <summary> /// 头像 /// </summary> public Image Avatar { get; set; } /// <summary> /// 角色 /// </summary> [MaxLength(32)] public string Role { get; set; } /// <summary> /// 部门ID /// </summary> public int DepartmentID { get; set; } } }
使用T4模板生成实体类还需要注意一下几点:
a.需要手动添加必要的引用,例如上面的 public Image Avatar { get; set; }需要添加System.Drawing。
b.每次项目内T4模板保存时都会依据excel设计文件重新生成实体类并覆盖原代码,故数据库变动时应该修改excel设计文件然后重新使用T4模板生成实体类,不应仅修改生成的实体类,否则T4模板保存时会覆盖修改。