程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Visual Studio DSL 入門 13---結合T4生成代碼

Visual Studio DSL 入門 13---結合T4生成代碼

編輯:關於.NET

在前面的幾節裡,我們已經完成了一個簡單的狀態機設計器,通過這個狀態機可以設計出一 個狀態流,但是如果只是這樣,我們直接使用UML設計工具就行了,何必自己開發呢? 我們走的 是模型驅動開發路線,呵呵,注意哥說的是開發,不是設計.這一節就和我們的開發聯系起來,生 成符合我們要求的代碼.

結合vs.net dsl生成代碼有以下幾種方式:

直接硬編碼,在代碼裡面利用模型拼接生成的代碼,我記得activewriter就是這樣做的生成 nhibernate代碼.

結合模板引擎,你可以使用xslt或者t4(text template  transformation toolkit),或者 是codesmith等.

在這裡我們使用T4來生成,vs.net已經內置支持T4引擎(dsl和linq等都是使用t4來生成 的), 即使這樣,vs.net也沒有內置對T4文件的編輯器,在開始下面之前,需要從這裡下載免費 的Community版本安裝.

1.直接運行我們的項目,可以發現在Debugging項目下面有兩個tt文件,這兩個文件就是生 成簡單代碼的一個例子,直接打開LanguageSmReport.tt

<#@ Import Namespace="System" #>
<#@ template  inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation " language="C#" #>
<#@ output extension=".txt" #>
<#@ LanguageSm processor="LanguageSmDirectiveProcessor"  requires="fileName='Test.mydsl5'" #>

<#
    foreach (State state in StateMachine.States) {
#>
   <#=   state.Name #>
<#
    }
#>

2.運行自定義工具,生成的文件就是附屬的txt文件:

Draft
   NewOrder
   Cancelled
   Confirmed

3. 對應的我們的狀態機是我建立的一個簡單的訂單狀態流轉:

4.回頭過來再看一下這個t4模板文件,看起來其實很象aspx頁面:

(1).通過Import引用需要的命名空間,事先所在的dll一定要添加到項目中.

(2).第二行指定模板繼承自 Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation,指定模板 語言使用C#,注意,如果你需要使用framework 3.5,這裡需要設置成C#3.5.

(3).通過output指令設置生成文件的後綴名和編碼.

(4).聲明我們的指令處理器以及需要加載的模型文件.

(5).模板的正文很容易理解,只需要記住它的幾個控制塊的類型.

<#….#>標准控制塊,裡面放控制語句,就是我們普通的C#或者VB代碼組成的控制 語句.

<#+..#>類特性控制塊,裡面可以添加方法,屬性,域或者內嵌類,在這裡一般放 一些重用性高的代碼.

<#=…#>表達式控制塊,計算裡面包含的表達式的值並輸出.

5.但是這個T4文件又是怎麼樣的運行解析機制呢,其實它和我們的aspx頁面很類似,我們 來看一下它生成的轉換類:

 public class GeneratedTextTransformation : Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation {

public override string TransformText() {

try {

this.Write("\r\n");

this.Write("\r\n");

this.Write("\r\n");

this.Write("\r\n\r\n");

foreach (State state in StateMachine.States) {

this.Write("\r\n\t");

this.Write(Microsoft.VisualStudio.TextTemplating.ToStringHelper.ToStringWithCulture(

state.Name

));

this.Write("\r\n");

}

this.Write("\r\n");

} catch (System.Exception e) {

System.CodeDom.Compiler.CompilerError error = new System.CodeDom.Compiler.CompilerError();

error.ErrorText = e.ToString();

error.FileName = "template2.t4";

this.Errors.Add(error);

}

return this.GenerationEnvironment.ToString();

}

private Company.LanguageSm.StateMachine statemachineValue;

private Company.LanguageSm.StateMachine StateMachine {

get {

return this.statemachineValue;

}

}

protected override void Initialize() {

this.AddDomainModel(typeof(Microsoft.VisualStudio.Modeling.Diagrams.CoreDesignSurfaceDomainModel));

this.AddDomainModel(typeof(Company.LanguageSm.LanguageSmDomainModel));

base.Initialize();

if ((this.Errors.HasErrors == false)) {

Microsoft.VisualStudio.Modeling.Transaction statemachineTransaction = null;

try {

Microsoft.VisualStudio.Modeling.SerializationResult serializationResult = new Microsoft.VisualStudio.Modeling.SerializationResult();

statemachineTransaction = this.Store.TransactionManager.BeginTransaction("Load", true);

this.statemachineValue = Company.LanguageSm.LanguageSmSerializationHelper.Instance.LoadModel(serializationResult, this.Store, "Test.mydsl5", null, null);

if (serializationResult.Failed) {

throw new Microsoft.VisualStudio.Modeling.SerializationException(serializationResult);

}

statemachineTransaction.Commit();

} finally {

if ((statemachineTransaction != null)) {

statemachineTransaction.Dispose();

}

}

}

}

}

通過Write方法輸出我的內容,然後其實是對於我們的模型的根域類屬性,並重寫 Initialize()方法進行了初始化.

6.回過頭來,我們要根據上面3中的訂單狀態圖,生成我們的代碼,最重要也是最基本的 一點,在你打算用T4生成代碼時,你一定要對你想生成的代碼了如指掌.如果你連自己要什麼 都不知道,更不可能達到了. 我們以最基本的一個例子,雖然這樣寫代碼可能並不合理,不 過在這裡我們為了使問題盡量簡單化:

/// <summary>
    /// 訂單狀態
    /// </summary>
     public enum OrderStateEnum
     {
     Draft,
     NewOrder,
     Cancelled,
     Confirmed,
     }

    /// <summary>
    /// 訂單生成
    /// </summary>
    public partial class Order
    {
      public OrderStateEnum OrderState
      {
       get;
       set;
      }

     public Order()
     {

     }

     protected void  SaveOrder(Order order)
     {

        if(order.OrderState == OrderStateEnum.Draft)
             order.OrderState = OrderStateEnum.NewOrder;

     }
    }

(1).我們需要為我們的所有的狀態生成到我們的枚舉類型OrderStateEnum中(狀態名就是 枚舉名,值不考慮).

(2).在我們的Order類中,有一個固定的OrderStateEnum類型的屬性OrderState,表示當前 訂單的狀態.

(3).需要為我們的每一個轉移Transigion生成一個方法到我們的Order類中,方法名就是 Transition的Event的值,方法體就是當訂單的狀態為Transigion的發起者Predecessor時, 將訂單的狀態置為目標Successor.也就是說在SaveOrder內,判斷如果訂單的狀態是Draft時 ,就把訂單的狀態置為NewOrder.

7.在明白了目標代碼後,我們來寫我們的T4文件,首先需要添加一個公共的方法來獲取 StateMachine裡的所有的Transition.我們使用<#+#>來完成這個方法,注意這個方法 需要放在整個模板文件的最下面.

<#+
     System.Collections.Generic.IEnumerable<Transition>  GetAllTransitions() {
       foreach (State s in StateMachine.States)
         foreach (Transition t in Transition.GetLinksToSuccessors (s))
           yield return t;
     }
#>

8.剩下的工作就更簡單了,我們只需要遍歷這些Transition,對於每個Transition,對於 它的Predessor和Successor進行如上所說的判斷和賦值即可,而對於固定的部分,我們只需要 以文本的形式寫出來就可以了:

/// <summary>
    /// 訂單狀態
    /// </summary>
     public enum OrderStateEnum
     {
<#
   PushIndent("  ");
   foreach (State state in StateMachine.States)
     WriteLine("  {0},", state.Name);
   PopIndent();
#>
     }
    /// <summary>
    /// 訂單生成
    /// </summary>
    public partial class Order
    {
      public OrderStateEnum OrderState
      {
       get;
       set;
      }

     public Order()
     {

     }

     public  void Save(Order order){
       // to save order
     }

     <#
        foreach (Transition transition in GetAllTransitions())  {
     #>

     protected void  <#=transition.Event#>
     {

        if(order.OrderState ==  OrderStateEnum.<#=transition.Predecessor.Name#>)
             order.OrderState = OrderStateEnum.<#=  transition.Successor.Name#>;
       Save(order);
     }
<#
        }
#>
   }

9.轉換模板,就可以看到我們生成的代碼了,雖然在這個例子中並沒有顯現出T4的強大, 不過對於復雜的規范性的系統來說,能夠通過Dsl進行設計,然後結合T4生成那些代碼還是能 夠極大的提高生產率的.

/// <summary>
    /// 訂單狀態
    /// </summary>
     public enum OrderStateEnum
     {
     Draft,
     NewOrder,
     Cancelled,
     Confirmed,
     }
    /// <summary>
    /// 訂單生成
    /// </summary>
    public partial class Order
    {
      public OrderStateEnum OrderState
      {
       get;
       set;
      }

     public Order()
     {

     }

     public  void Save(Order order){
       // to save order
     }

     protected void  SaveOrder(Order order)
     {

        if(order.OrderState == OrderStateEnum.Draft)
             order.OrderState = OrderStateEnum.NewOrder;
       Save(order);
     }

     protected void  CancelOrder(Order order)
     {

        if(order.OrderState == OrderStateEnum.NewOrder)
             order.OrderState = OrderStateEnum.Cancelled;
       Save(order);
     }

     protected void  ConfirmOrder(Order order)
     {

        if(order.OrderState == OrderStateEnum.NewOrder)
             order.OrderState = OrderStateEnum.Confirmed;
       Save(order);
     }

   }

10.需要注意的是,結合dsl和t4也不可能使你的一個項目不用手寫代碼了,它只能是能夠 生成你比較固定的代碼部分,能夠抽象出來的,目前t4還不能夠解決生成代碼中允許直接簽 入自定義代碼,所以現在一般以文件為分隔,通過partial機制,由t4生成的cs文件中專門存 儲這些抽象出來的可生成部分,而這部分是不允許修改的,因為在你修改完模型後下次還會 重新生成,而你需要擴展的部分,都以partial類的機制在另外一個類中.

代碼下載: http://files.cnblogs.com/lonely7345/LanguageSm5.rar

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved