從文字範本存取模型
您可以使用文字範本建立以特定領域語言模型為基礎的報告檔案、原始程式碼檔案和其他文字檔。 如需文字範本的詳細資訊,請參閱 程式碼產生和 T4文字範本。 當您偵錯 DSL 時,文字範本將會在實驗模式中運作,而且也會在您已部署 DSL 的電腦上運作。
注意
當您建立 DSL 解決方案時,會在偵錯專案中產生樣本文字範本 *.tt 檔案。 當您變更網域類別的名稱時,這些範本將無法再運作。 不過,它們包含您需要的基本指示詞,並提供您可以更新以符合 DSL 的範例。
若要從文字範本存取模型:
將範本指示詞的繼承屬性設定為 Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation。 這會提供 Store 的存取權。
指定您想要存取之 DSL 的指示詞處理器。 這會載入 DSL 的組件,讓您可以在文字範本的程式碼中使用其網域類別、屬性和關聯性。 它也會載入您指定的模型檔案。
當您從 DSL 最小語言範本建立新的 Visual Studio 解決方案時,會在偵錯專案中建立類似下列範例的
.tt
檔案。
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>
<#@ output extension=".txt" #>
<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1'" #>
This text will be output directly.
This is the name of the model: <#= this.ModelRoot.Name #>
Here is a list of elements in the model:
<#
// When you change the DSL Definition, some of the code below may not work.
foreach (ExampleElement element in this.ExampleModel.Elements)
{#>
<#= element.Name #>
<#
}
#>
請注意有關這個範本的下列重點:
範本可以使用您在 DSL 定義中定義的網域類別、屬性和關聯性。
範本會載入您在
requires
屬性中指定的模型檔案。this
中的屬性包含根項目。 您的程式碼可以從該處瀏覽至模型的其他元素。 屬性的名稱通常與 DSL 的根領域類別相同。 在此範例中,它是this.ExampleModel
。雖然撰寫程式碼片段的語言是 C#,但您可以產生任何類型的文字。 您也可以在 Visual Basic 中撰寫程式碼,方法是將 屬性
language="VB"
新增至template
指示詞。若要對範本進行偵錯,請將
debug="true"
新增至template
指示詞。 如果發生例外狀況,範本將會在另一個 Visual Studio 執行個體中開啟。 如果您想要在程式碼中的特定點中斷偵錯工具,請插入語句System.Diagnostics.Debugger.Break();
如需詳細資訊,請參閱 偵錯 T4 文字範本。
關於 DSL 指示詞處理器
範本可以使用您在 DSL 定義中定義的網域類別。 這是由通常出現在範本開頭附近的指示詞所帶來。 在上一個範例中,如下所示。
<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1'" #>
指示詞的名稱 (在此範例中為 MyLanguage
) 衍生自 DSL 的名稱。 它會叫用作為 DSL 的一部分產生的 指示詞處理器。 您可以在 Dsl\GeneratedCode\DirectiveProcessor.cs 中找到其原始程式碼。
DSL 指示詞處理器會執行兩個主要工作:
它會有效地將組件和匯入指示詞插入參考 DSL 的範本中。 這可讓您在範本程式碼中使用您的網域類別。
它會載入您在
requires
參數中指定的檔案,並在this
中設定屬性,該屬性會參考已載入模型的根項目。
在執行範本之前驗證模型
您可以在執行範本之前,先驗證模型。
<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1';validation='open|load|save|menu'" #>
請注意:
filename
和validation
參數會以「;」分隔,而且不能有其他分隔符號或空格。驗證類別清單會決定要執行的驗證方法。 多個類別應該以「|」分隔,而且不能有其他分隔符號或空格。
如果找到錯誤,則會在錯誤視窗中報告錯誤,而結果檔案將會包含錯誤訊息。
從文字範本存取多個模型
注意
這個方法可讓您在相同的範本中讀取多個模型,但不支援 ModelBus 參考。 若要讀取以 ModelBus 參考相互連結的模型,請參閱 在文字範本中使用 Visual Studio ModelBus。
如果您想要從相同的文字範本存取多個模型,則必須針對每個模型呼叫產生的指示詞處理器一次。 您必須在 requires
參數中指定每個模型的檔案名稱。 您必須指定您希望 provides
參數中根領域類別所使用的名稱。 您必須為每個指示詞呼叫中的 provides
參數指定不同的值。 例如,假設您有三個稱為「Library.xyz」、「School.xyz」和「Work.xyz」的模型檔案。 若要從相同的文字範本存取它們,您必須撰寫三個類似下列的指示詞呼叫。
<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='Library.xyz'" provides="ExampleModel=LibraryModel" #>
<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='School.xyz'" provides="ExampleModel=SchoolModel" #>
<#@ ExampleModel processor="<YourLanguageName>DirectiveProcessor" requires="fileName='Work.xyz'" provides="ExampleModel=WorkModel" #>
注意
此範例程式碼適用於以最小語言解決方案範本為基礎的語言。
若要存取文字範本中的模型,您現在可以撰寫類似下列範例中的程式碼。
<#
foreach (ExampleElement element in this.LibraryModel.Elements)
...
foreach (ExampleElement element in this.SchoolModel.Elements)
...
foreach (ExampleElement element in this.WorkModel.Elements)
...
#>
動態載入模型
如果您想要在執行階段判斷要載入的模型,您可以在程式碼中動態載入模型檔案,而不是使用 DSL 特定的指示詞。
不過,DSL 特定指示詞的其中一個函式是匯入 DSL 命名空間,讓範本程式碼可以使用該 DSL 中定義的網域類別。 因為您不是使用指示詞,因此您必須為可能載入的所有模型新增 <組件> 並 <匯入> 指示詞。 如果可能載入的不同模型都是相同 DSL 的執行個體,那麽這會很容易。
若要載入檔案,最有效的方法是使用 Visual Studio ModelBus。 在一般案例中,您的文字範本會使用 DSL 特定指示詞,以一般的方式載入第一個模型。 該模型會包含另一個模型的 ModelBus 參考。 您可以使用 ModelBus 開啟參考的模型並存取特定元素。 如需詳細資訊,請參閱 在文字範本中使用 Visual Studio ModelBus。
在較不平常的案例中,您可能想要開啟只有檔案名稱的模型檔案,而該檔案可能不在目前的 Visual Studio 專案中。 在此情況下,您可以使用 [如何: 在程式碼中開啟檔案的模型] 中所述的技術來開啟檔案。
從範本產生多個檔案
如果您想要產生數個檔案,例如,若要為模型中的每個元素產生個別的檔案,有幾個可能的方法。 根據預設,每個範本檔案只會產生一個檔案。
分割長檔案
您會透過這個方法使用範本來產生單一檔案,並以分隔符號分隔。 然後,您會將檔案分割成其組件。 有兩個範本,一個用來產生單一檔案,另一個用來分割它。
LoopTemplate.t4 會產生長單一檔案。 請注意,其副檔名為「.t4」,因為當您按一下 [轉換所有範本] 時,不應該直接處理它。 此範本會採用參數,指定分隔區段的分隔符號字串:
<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" #>
<#@ parameter name="delimiter" type="System.String" #>
<#@ output extension=".txt" #>
<#@ MyDSL processor="MyDSLDirectiveProcessor" requires="fileName='SampleModel.mydsl1';validation='open|load|save|menu'" #>
<#
// Create a file segment for each element:
foreach (ExampleElement element in this.ExampleModel.Elements)
{
// First item is the delimiter:
#>
<#= string.Format(delimiter, element.Id) #>
Element: <#= element.Name #>
<#
// Here you generate more content derived from the element.
}
#>
LoopSplitter.tt
會叫用 LoopTemplate.t4
,然後將產生的檔案分割成其區段。 請注意,此範本不一定是模型範本,因為它不會讀取模型。
<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="System.Runtime.Remoting.Messaging" #>
<#@ import namespace="System.IO" #>
<#
// Get the local path:
string itemTemplatePath = this.Host.ResolvePath("LoopTemplate.t4");
string dir = Path.GetDirectoryName(itemTemplatePath);
// Get the template for generating each file:
string loopTemplate = File.ReadAllText(itemTemplatePath);
Engine engine = new Engine();
// Pass parameter to new template:
string delimiterGuid = Guid.NewGuid().ToString();
string delimiter = "::::" + delimiterGuid + ":::";
CallContext.LogicalSetData("delimiter", delimiter + "{0}:::");
string joinedFiles = engine.ProcessTemplate(loopTemplate, this.Host);
string [] separateFiles = joinedFiles.Split(new string [] {delimiter}, StringSplitOptions.None);
foreach (string nameAndFile in separateFiles)
{
if (string.IsNullOrWhiteSpace(nameAndFile)) continue;
string[] parts = nameAndFile.Split(new string[]{":::"}, 2, StringSplitOptions.None);
if (parts.Length < 2) continue;
#>
Generate: [<#= dir #>] [<#= parts[0] #>]
<#
// Generate a file from this item:
File.WriteAllText(Path.Combine(dir, parts[0] + ".txt"), parts[1]);
}
#>