• 三、T4模板与实体生成


         上文我们最后虽然用模板创建了一个实体类,但是类的内容仍旧是静态的,这里我们需要用动态方式生成类的内容。因为需要查询数据库这里又免不了各种繁琐的连接数据库操作,为了使我们的编码更加直观,仍然采用C#编码习惯来书写T4代码。

         在JSP中,我们可以使用include标签来包含另一个JSP文件。在T4模板中也可以在一个模板文件中包含另一个模板文件。所以我们尽可能把公共模块的代码提取到一个T4模板文件中,方便重复使用。包含指令如下:

    <#@ include file="$(ProjectDir)IncludeFile.tt" #>

    使用的@ include指令。其中$(ProjectDir)是宏变量,指代当前项目工程路径。

    首先创建一个Utility.tt模板文件,该文件主要放一些常用的工具类如DBHelper等,因为工具类只在调用时才执行,而.tt文件在保存时就执行,所以每次保存该文件都会执行里面的代码很繁琐,可以右击该文件【属性】把【自定义工具】中:TextTemplatingFileGenerator属性去掉。这样该模板保存时就不会执行。(注:关于设计时模板和运行时模板的区别和用途这里就不做介绍,这样做主要把问题简化)完整代码如下:

    <#+
    //数据库操作
    public static class DBHelper
    {
        private static SqlCommand PrepareCommand(SqlConnection conn, string cmdText, CommandType cmdType, SqlParameter[] parameters)
        {
            if (conn.State != ConnectionState.Open)
            {
                conn.Open();
            }
            SqlCommand cmd = new SqlCommand(cmdText, conn);
            cmd.CommandType = cmdType;
            if (parameters != null)
            {
                cmd.Parameters.Clear();
                cmd.Parameters.AddRange(parameters);
            }
    
            return cmd;
        }
    
        public static DataSet GetDataSet(string connString,string strSQL)
        {
            using (SqlConnection conn = new SqlConnection(connString))
            {
                SqlDataAdapter da = new SqlDataAdapter(strSQL, conn);
                DataSet ds = new DataSet();
                da.Fill(ds);
                return ds;
            }
        }
    
        public static DataSet GetSchemaInfo(string connString,string tableName)
        {
                StringBuilder sbSQL = new StringBuilder();
                sbSQL.AppendLine("SELECT ");
                sbSQL.AppendLine("    [Id]=C.column_id,");
                sbSQL.AppendLine("    [Name]=C.name,");
                sbSQL.AppendLine("    [Type]=T.name,");
                sbSQL.AppendLine("    [Length]=C.max_length,");
                sbSQL.AppendLine("    [Identity]=CASE WHEN C.is_identity=1 THEN N'T'ELSE N'' END,");
                sbSQL.AppendLine("    [PrimaryKey]=ISNULL(PKInfo.PrimaryKey,N''),");
                sbSQL.AppendLine("    [ForeignKey]=CASE WHEN FKInfo.parent_column_id>0 THEN N'T'ELSE N'' END,");
                sbSQL.AppendLine("    [ForeignKeyTable]=ISNULL(FKInfo.name,N''),");
                sbSQL.AppendLine("    [IsNull]=CASE WHEN C.is_nullable=1 THEN N'T'ELSE N'' END,");
                sbSQL.AppendLine("    [Default]=ISNULL(DC.definition,N''),");
                sbSQL.AppendLine("    [ColumnDesc]=ISNULL(EP.value,N'') ");
                sbSQL.AppendLine("FROM sys.columns C ");
                sbSQL.AppendLine("INNER JOIN sys.objects O ON C.object_id=o.object_id AND O.type='U' AND O.is_ms_shipped=0 ");
                sbSQL.AppendLine("INNER JOIN sys.types T ON C.user_type_id=T.user_type_id ");
                sbSQL.AppendLine("LEFT JOIN sys.default_constraints DC ON C.object_id=DC.parent_object_id AND C.column_id=DC.parent_column_id AND C.default_object_id=DC.object_id ");
                sbSQL.AppendLine("LEFT JOIN sys.extended_properties EP ON EP.class=1 AND C.object_id=EP.major_id AND C.column_id=EP.minor_id ");
                sbSQL.AppendLine("LEFT JOIN (SELECT IC.object_id,IC.column_id,PrimaryKey=CASE WHEN I.is_primary_key=1 THEN N'T'ELSE N'' END FROM sys.indexes I INNER JOIN sys.index_columns IC ON I.[object_id]=IC.[object_id] AND I.index_id=IC.index_id)PKInfo ON PKInfo.object_id=C.object_id AND PKInfo.column_id=C.column_id ");
                sbSQL.AppendLine("LEFT JOIN (SELECT FKC.parent_object_id,FKC.parent_column_id,O.name FROM sys.foreign_key_columns FKC INNER JOIN sys.objects O ON FKC.referenced_object_id=O.object_id)FKInfo ON C.object_id=FKInfo.parent_object_id AND C.column_id=FKInfo.parent_column_id ");
                sbSQL.AppendFormat("WHERE O.name='{0}' ORDER BY Id ASC", tableName);
                return GetDataSet(connString,sbSQL.ToString());
        }
    }
    
    //类型转换
    public static class TypeConvertor
    {
        //将数据库类型映射成C#类型
        public static string MapType(string dbType)
        { 
            if (string.IsNullOrEmpty(dbType)) return dbType;
            dbType = dbType.ToLower(); 
            string csType = "object";
            switch (dbType)
            {
                case "char": csType = "string"; break; 
                case "date": csType = "DateTime"; break; 
                case "datetime": csType = "DateTime"; break; 
                case "decimal": csType = "decimal"; break; 
                case "float": csType = "double"; break; 
                case "int": csType = "int"; break; 
                case "money": csType = "decimal";break; 
                case "nchar": csType = "string"; break; 
                case "ntext": csType = "string"; break; 
                case "nvarchar": csType = "string"; break; 
                case "smalldatetime": csType = "DateTime"; break; 
                case "smallint": csType = "short"; break; 
                case "smallmoney": csType = "decimal"; break; 
                case "text": csType = "string"; break; 
                case "time": csType = "TimeSpan"; break; 
                case "varchar": csType = "string"; break; 
                default: csType = "object"; break;
            } 
            return csType;
        }
    }
    #>

    该文件把自动生成的指令标签全部去掉了,只用了<#+ #>标签来包含代码块。该文件中包含两个类:DBHelper和TypeConvertor,为了方便演示我简化了这些类中的代码,只留了需要用到的方法。接下来打开原来的EntityTemplate.tt文件修改代码如下:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ assembly name="System.Data" #>
    <#@ assembly name="System.Xml" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="System.Data" #>
    <#@ import namespace="System.Data.SqlClient" #>
    <#@ import namespace="System.Xml" #>
    <#@ output extension=".cs" #>
    <#@ include file="$(ProjectDir)Utility.tt" #>
    <#
        string connString="Data Source=192.168.1.101;Database=DB_Test;uid=sa;pwd=123";
        string tableName="Base_Person";
        string nameSpace="EntityGenerator";
    #>
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace <#=nameSpace  #>
    {
        public class <#= tableName #>Entity
        {
    <# 
    DataTable dt = DBHelper.GetSchemaInfo(connString,tableName).Tables[0];
    foreach(DataRow dr in dt.Rows)
    {
     #>
            /// <summary>
            /// <#= dr["ColumnDesc"].ToString() #>
            /// </summary>
            public <#= TypeConvertor.MapType(dr["Type"].ToString()) #> <#= dr["Name"].ToString() #> { get; set; }
    
    <# } #>
        }
    }

    该文件中使用<#@ include file="$(ProjectDir)Utility.tt" #>来包含上述定义的Utility模板。

    <#@ assembly name="System.Core" #>
    <#@ assembly name="System.Data" #>
    <#@ assembly name="System.Xml" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    <#@ import namespace="System.Data" #>
    <#@ import namespace="System.Data.SqlClient" #>
    <#@ import namespace="System.Xml" #>
    这里添加数据库操作类需要使用的程序集和命名空间。
    <#
        string connString="Data Source=192.168.1.101;Database=MicroERP;uid=sa;pwd=123qwe!@#";
        string tableName="Base_Person";
        string nameSpace="EntityGenerator";
    #>

    这里我们把需要用的变量提成全局,以方便后面变更数据库、项目时修改。

    <#= #>这和ASP.NET一样为输出表达式。

    <# 
    DataTable dt = DBHelper.GetSchemaInfo(connString,tableName).Tables[0];
    foreach(DataRow dr in dt.Rows)
    {
     #>
            /// <summary>
            /// <#= dr["ColumnDesc"].ToString() #>
            /// </summary>
            public <#= TypeConvertor.MapType(dr["Type"].ToString()) #> <#= dr["Name"].ToString() #> { get; set; }
    
    <# } #>

    这里就是连接数据库获取到表结构,再根据表结构,动态生成类的属性,注释,以及数据库类型转化为C#类型。保存该模板效果如下:

    image

    这样一个完整的实体类就动态生成了,关于获取数据库表结构,后面将花一整个篇幅来讲解不同数据库怎么提取表结构。估计也有可能放在ORM框架中讲解,这里仅仅先演示其作用。

    源码下载

  • 相关阅读:
    【嵌入式】arm-linux-gcc/ld/objcopy/objdump参数概述
    【Java】Java复习笔记-第四部分
    【C/C++】C语言复习笔记-17种小算法-解决实际问题
    【Java】Java复习笔记-三大排序算法,堆栈队列,生成无重复的随机数列
    【Java】Java复习笔记-第三部分
    【教程】ubuntu下安装NFS服务器
    【Java】Java复习笔记-第二部分
    【Java】Java复习笔记-第一部分
    【教程】ubuntu下安装samba服务器
    【C/C++】一道试题,深入理解数组和指针
  • 原文地址:https://www.cnblogs.com/UltimateAvalon/p/4602046.html
Copyright © 2020-2023  润新知