T4模板+Excel数据表生成实体类

时间:2024-02-19 17:31:08

  最近开始新的项目,在数据库设计时使用了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模板保存时会覆盖修改。