VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > C#编程 >
  • C#教程之C# ORM学习笔记:T4入门及生成数据库实体类

本站最新发布   C#从入门到精通
试听地址  
https://www.xin3721.com/eschool/CSharpxin3721/

 一、什么是T4?

    1.1、T4简介

    T4,即4个T开头的英文字母组合:Text Template Transformation Toolkit,是微软官方在Visual Studio 2008开始使用的代码生成引擎。T4是由一些文本块和控制逻辑组成的混合模板,简单地说,T4可以根据模板生成您想要的文件,如类文件、文本文件、HTML等等。

    VS提供了一套基于T4引擎的代码生成执行环境,由以下程序集构成:

    Microsoft.VisualStudio.TextTemplating.10.0.dll

    Microsoft.VisualStudio.TextTemplating.Interfaces.10.0.dll

    Microsoft.VisualStudio.TextTemplating.Modeling.10.0.dll

    Microsoft.VisualStudio.TextTemplating.VSHost.10.0.dll

    1.2、T4模板类型

    T4模板有两种类型:

    1)运行时模板

    在应用程序中执行运行时T4文本模板,以便生成文本字符串。

    若要创建运行时模板,请向您的项目中添加"运行时文本模板"文件。另外,您还可以添加纯文本文件并将其"自定义工具"属性设置为"TextTemplatingFilePreprocessor"。

    2)设计时模板

    在VS中执行设计时T4文本模板,以便定义应用程序的部分源代码和其它资源。

    若要创建设计时模板,请向您的项目中添加"文本模板"文件。 另外,您还可以添加纯文本文件并将其"自定义工具"属性设置为"TextTemplatingFileGenerator"。

    1.3、插件安装

    VS默认的编辑工具无高亮、无提示,错误不容易定位,建议安装tangible T4 Editor插件进行编写T4代码。

    二、T4 Hello World示例

    假设有一个控制台应用程序LinkTo.Test.ConsoleT4,现在要输出"Hello World"字符串,Program代码如下:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkTo.Test.ConsoleT4
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            Console.Read();
        }
    }
}
复制代码

    现在把Program.cs文件删除掉,利用T4模板生成一个与上述代码相同的Program.cs,操作方法如下:

    1)项目右键"添加"->"新建项"->"文本模板",将名称更改为Program.tt。

    2)Program.tt的代码如下:

复制代码
<#@ output extension=".cs" #>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

<# 
    string ClassName = "Program";
#>
namespace LinkTo.Test.ConsoleT4
{
    class <#=ClassName #>
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
            Console.Read();
        }
    }
}
复制代码

    3)点击保存,即可看到Program.tt下生成了一个Program.cs文件,代码与最初的Hello World一样。

    三、T4 Hello World示例扩展

    现在扩展一下Hello World示例,在程序中增加两个类:

    1)Hello类,输出"Hello"。

    2)World类,输出"World"。

    代码如下:

复制代码
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
<# 
    string ClassName = "Program";
#>
<# 
    List<string> classNames = new List<string>() {"Hello","World"};
    List<string> callMethods = new List<string>();
#>

namespace LinkTo.Test.ConsoleT4
{
<#    
foreach (string className in classNames)
{
    callMethods.Add($"{className}.Show();");
#>
    class <#=className #>
    {
        /// <summary>
        /// <#=className #>类Show()方法
        /// </summary>
        public static void Show()
        {
            Console.WriteLine("<#=className #>");
        }
    }
    
<#
}
#>
    class <#=ClassName #>
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");

<#
        foreach (string callMethod in callMethods)
        {
#>
            //<#=callMethod #>方法调用
            <#=callMethod #>
<#
        }
#>

            Console.Read();
        }
    }
}
复制代码

    生成文件如下:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkTo.Test.ConsoleT4
{
    class Hello
    {
        /// <summary>
        /// Hello类Show()方法
        /// </summary>
        public static void Show()
        {
            Console.WriteLine("Hello");
        }
    }
    
    class World
    {
        /// <summary>
        /// World类Show()方法
        /// </summary>
        public static void Show()
        {
            Console.WriteLine("World");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");

            //Hello.Show();方法调用
            Hello.Show();
            //World.Show();方法调用
            World.Show();

            Console.Read();
        }
    }
}
复制代码

    四、T4模板的基本结构

    代码块可分为两种:文本及程序脚本。

    4.1、文本:就是需要生成的文本

    4.2、程序脚本:内部执行,最终生成想要的文本。T4中<# #>中的部分,都属于程序脚本内容。

    为了方便理解,使用"块"(Block)来细分语法。块是构成T4模板的基本单元,可以分成5类:指令块(Directive Block)、文本块(Text Block)、代码语句块(Statement Block)、表达式块(Expression Block)、类特性块(Class Feature Block)。

    4.2.1、指令块(Directive Block)

    和ASP.NET页面的指令一样,它们出现在文件头,通过<#@ … #>表示。其中<#@ template … #>指令是必须的,用于定义模板的基本属性,比如编程语言、基于的文化、是否支持调式等。

    指令通常是模板文件或包含的文件中的第一个元素。不应将它们放置在代码块<#...#>内,也不应放置在类功能块<#+...#>之后。

    T4 模板指令

    <#@ template [language="C#"] [hostspecific="true"] [debug="true"] [inherits="templateBaseClass"] [culture="code"] [compilerOptions="options"] #>

    T4 参数指令

    <#@ parameter type="Full.TypeName" name="ParameterName" #>

    T4 输出指令

    <#@ output extension=".fileNameExtension" [encoding="encoding"] #>

    T4 程序集指令

    <#@ assembly name="[assembly strong name|assembly file name]" #>

    $(SolutionDir):当前项目所在解决方案目录

    $(ProjectDir):当前项目所在目录

    $(TargetPath):当前项目编译输出文件绝对路径

    $(TargetDir):当前项目编译输出目录

    T4 导入指令

    <#@ import namespace="namespace" #>

    T4 包含指令

    <#@ include file="filePath" #>

    4.2.2、文本块(Text Block)

    文本块就是直接原样输出的静态文本,不需要添加任何的标签。

    4.2.3、代码语句块(Statement Block)

    代码语句块通过<# Statement #>的形式表示,中间是一段通过相应编程语言编写的程序调用,我们可以通过代码语句快控制文本转化的流程。

    4.2.4、表达式块(Expression Block)

    表达式块以<#=Expression #>的形式表示,通过它可以动态地解析字符串表达式内嵌到输出的文本中。

    4.2.5、类特性块(Class Feature Block)

    如果文本转化需要一些比较复杂的逻辑,代码可能需要写在一个单独的辅助方法中,甚至是定义一些单独的类。类特性块的表现形式为<#+ FeatureCode #>。

    五、T4模板生成数据库实体类

    5.1、添加一个T4Code文件夹,并在下面新建两个文本模板,需要注意的是,这里不使用默认的.tt扩展名,而是.ttinclude,将它们作为包含文件使用。

复制代码
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data" #>
<#@ assembly name="$(ProjectDir)\Lib\MySql.Data.Dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data"#>
<#@ import namespace="System.Data.SqlClient"#>
<#@ import namespace="MySql.Data.MySqlClient"#>
<#+
    #region T4Code
    /// <summary>
    /// 数据库架构接口

    /// </summary>
    public interface IDBSchema : IDisposable
    {
        List<string> GetTableList();
        Table GetTableMetadata(string tableName);
    }

    /// <summary>
    /// 数据库架构工厂

    /// </summary>
    public class DBSchemaFactory
    {
        static readonly string DatabaseType = "SqlServer";
        public static IDBSchema GetDBSchema()
        {
            IDBSchema dbSchema;
            switch (DatabaseType) 
            {
                case "SqlServer":
                    {
                        dbSchema =new SqlServerSchema();
                        break;
                    }
                case "MySql":
                    {
                        dbSchema = new MySqlSchema();
                        break;
                    }
                default: 
                    {
                        throw new ArgumentException("The input argument of DatabaseType is invalid.");
                    }
            }
            return dbSchema;
        }
    }

    /// <summary>
    /// SqlServer
    /// </summary>
    public class SqlServerSchema : IDBSchema
    {
        public string ConnectionString = "Server=.;Database=CFDEV;Uid=sa;Pwd=********;";
        public SqlConnection conn;

        public SqlServerSchema()
        {
            conn = new SqlConnection(ConnectionString);
            conn.Open();
        }

        public List<string> GetTableList()
        {
            DataTable dt = conn.GetSchema("Tables");
            List<string> list = new List<string>();
            foreach (DataRow row in dt.Rows)
            {
                list.Add(row["TABLE_NAME"].ToString());
            }
            return list;
        }
        
        public Table GetTableMetadata(string tableName)
        {
            string commandText = string.Format("SELECT * FROM {0}", tableName); ;
            SqlCommand cmd = new SqlCommand(commandText, conn);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataSet ds = new DataSet();
            da.FillSchema(ds, SchemaType.Mapped, tableName);
            Table table = new Table(ds.Tables[0]);
            return table;
        }

        public void Dispose()
        {
            if (conn != null)
            {
                conn.Close();
            }
        }
    }

    /// <summary>
    /// MySql
    /// </summary>
    public class MySqlSchema : IDBSchema
    {
        public string ConnectionString = "Server=localhost;Port=3306;Database=ProjectData;Uid=root;Pwd=;";
        public MySqlConnection conn;

        public MySqlSchema()
        {
            conn = new MySqlConnection(ConnectionString);
            conn.Open();
        }

        public List<string> GetTableList()
        {
            DataTable dt = conn.GetSchema("Tables");
            List<string> list = new List<string>();
            foreach (DataRow row in dt.Rows)
            {
                list.Add(row["TABLE_NAME"].ToString());
            }
            return list;
        }

        public Table GetTableMetadata(string tableName)
        {
            string commandText = string.Format("SELECT * FROM {0}", tableName); ;
            MySqlCommand cmd = new MySqlCommand(commandText, conn);
            MySqlDataAdapter da = new MySqlDataAdapter(cmd);
            DataSet ds = new DataSet();
            da.FillSchema(ds, SchemaType.Mapped, tableName);
            Table table = new Table(ds.Tables[0]);
            return table;
        }

        public void Dispose()
        {
            if (conn != null)
            {
                conn.Close();
            }
        }
    }

    /// <summary>
    /// 数据表

    /// </summary>
    public class Table
    {
        public List<Column> PKs;
        public List<Column> Columns;
        public string DataTypes;

        public Table(DataTable dt)
        {
            PKs = GetPKList(dt);
            Columns = GetColumnList(dt);
            DataTypes = GetDataTypeList(Symbol.Normal);
        }

        public List<Column> GetPKList(DataTable dt)
        {
            List<Column> list = new List<Column>();
            Column column = null;
            if (dt.PrimaryKey.Length > 0)
            {
                list = new List<Column>();
                foreach (DataColumn dc in dt.PrimaryKey)
                {
                    column = new Column(dc);
                    list.Add(column);
                }
            }
            return list;
        }

        private List<Column> GetColumnList(DataTable dt)
        {
            List<Column> list = new List<Column>();
            Column column = null;
            foreach (DataColumn dc in dt.Columns)
            {
                column = new Column(dc);
                list.Add(column);
            }
            return list;
        }

        private string GetDataTypeList(Symbol symbol)
        {
            List<string> list = new List<string>();
            foreach (Column c in Columns)
            {
                if (symbol == Symbol.Normal)
                    list.Add(string.Format("{0} {1}", c.DataType, c.UpperColumnName));
                else if (symbol == Symbol.Underline)
                    list.Add(string.Format("{0} _{1}", c.DataType, c.UpperColumnName));
            }
            return string.Join(",", list.ToArray());
        }
    }

    /// <summary>
    /// 数据列

    /// </summary>
    public class Column
    {
        DataColumn columnBase;

        public Column(DataColumn _columnBase)
        {
            columnBase = _columnBase;
        }

        public string ColumnName { get { return columnBase.ColumnName; } }

        public string DataType
        { 
            get 
            {
                string result = string.Empty;
                if (columnBase.DataType.Name == "Guid")//for mysql,因为对于MySql如果是CHAR(36),类型自动为Guid。
                    result = "string";
                else if (columnBase.DataType.Name == "String")
                    result = "string";
                else if (columnBase.DataType.Name == "Int32")
                    result = "int";
                else
                    result = columnBase.DataType.Name;
                return result; 
            } 
        }

        public string MaxLength { get { return columnBase.MaxLength.ToString(); } }

        public bool AllowDBNull { get { return columnBase.AllowDBNull; } }

        public string UpperColumnName
        {
            get
            {
                return string.Format("{0}{1}", ColumnName[0].ToString().ToUpper(), ColumnName.Substring(1));
            }
        }

        public string LowerColumnName
        {
            get
            {
                return string.Format("{0}{1}", ColumnName[0].ToString().ToLower(), ColumnName.Substring(1));
            }
        }
    }

    /// <summary>
    /// 帮助类

    /// </summary>
    public class GeneratorHelper
    {
        public static readonly string StringType = "string";
        public static readonly string DateTimeType = "DateTime";
        public static string GetQuestionMarkByType(string typeName)
        {
            string result = typeName;
            if (typeName == DateTimeType)
            {
                result += "?";
            }
            return result;
        }
    }

    
      



  
相关教程