遇到的需求是有現成的XML資料定義檔如下

<?xml version="1.0" encoding="utf-8" ?>
<datatable>
  <table id="Running" version="1.0">
    <col name="RunDateTime" datatype="System.DateTime" pkey="Y"/>
    <col name="RunnerId"    datatype="System.String" pkey="Y"/>
    <col name="Record"        datatype="System.String"/>
  </table>
  <table id="Runner" version="1.0">
    <col name="RunnerId"    datatype="System.String" pkey="Y"/>
    <col name="Name"        datatype="System.String" />
    <col name="Birthday"    datatype="System.DateTime" />
  </table>
</datatable>

希望為其中每個Table各產生一個C#類別轡下:

using System.ComponentModel.DataAnnotations;
 
public class Runner
{
    [Key]
    public System.String RunnerId { get; set; }
    public System.String Name { get; set; }
    public System.DateTime Birthday { get; set; }
}

在過去,我會自己寫Code,解析XML文件再用StringBuilder組裝C#類別的內容。但既然已經學會T4範本程式產生器這等好物,自然沒有再走回頭路的道理。

由XML產生C#類別的邏輯很簡單,唯一的小挑戰是一般T4範例多是一個.tt檔產生一個.cs檔,而這個需求得依照XML定義一次產生多個.cs檔案。爬文找到範例,單一.tt要輸出多個檔案的原理,是在執行過程由GenerationEnvironment.ToString()取得當時累積的輸出結果,以File.WriteAllText()寫入檔案後,執行GenerationEnvironment.Clear()清掉已寫到檔案的內容,再執行下個類別的程式碼產生邏輯。

T4程式碼如下:

<#@ template  debug="true" hostSpecific="true" #>
<#@ output extension=".log" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ Assembly Name="System.Xml.dll" #>
<#@ Assembly Name="System.Xml.Linq.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #> 
<#
    string currPath = Path.GetDirectoryName(Host.TemplateFile);
    XDocument xd = XDocument.Load(Path.Combine(currPath, "marathon.xml"));
    foreach (var t in xd.Root.Elements()) {
        string tableName = t.Attribute("id").Value;
#>
using System.ComponentModel.DataAnnotations;
 
public class <#= tableName #>
{
<#
        foreach (var c in t.Elements()) {
            string name = c.Attribute("name").Value;
            string type = c.Attribute("datatype").Value;
            bool isPK = c.Attribute("pkey") != null;
            if (isPK) {
#>
    [Key]
<#
            }
#>
    public <#= type #> <#= name #> { get; set; }
<#
        }
#>
}
<#
        string relativeOutputFilePath = "Code\\" + tableName + ".cs";
        string outputFilePath = Path.Combine(currPath, relativeOutputFilePath);
        TemplateHelper.WriteTemplateOutputToFile(outputFilePath, GenerationEnvironment);
    }
#>
<#+
//REF: http://bit.ly/X9wTu0
public class TemplateHelper
{
    public static void WriteTemplateOutputToFile(
        string outputFilePath,
        System.Text.StringBuilder genEnvironment)
    {
        System.IO.File.WriteAllText(outputFilePath, genEnvironment.ToString());
        genEnvironment.Clear();
 
    }
}
#>

補充幾點:

  1. 預設T4並未參照System.Xml.dll及System.Xml.Linq.dll,記得要在最前方透過<#@ Assembly #>及<#@ import #>加入參照。
  2. 透過Host.TemplateFile可取得T4檔案所在實體路徑,可用來決定輸出檔案路徑。
  3. T4檔末端透過<#+ #>宣告了共用函數TemplateHelper.WriteTemplateOutputToFile(),透過其將產生結果寫入檔案。

Comments

# by nathan

請問這個能根據某SP回來的TABLE,建立其schema的cs檔嗎?

# by Jeffrey

to nathan, 我想到的做法是用ADO.NET執行SP取得DataTable,由DataColumn集合可取得欄位名稱及資料型別資訊,即能掌握Schema自動產生類別。

Post a comment