Udostępnij za pośrednictwem


Uzyskiwanie dostępu do modeli z szablonów tekstowych

Za pomocą szablonów tekstowych można tworzyć pliki raportów, pliki kodu źródłowego i inne pliki tekstowe oparte na modelach językowych specyficznych dla domeny. Aby uzyskać podstawowe informacje na temat szablonów tekstu, zobacz Generowanie kodu i Szablony tekstowe T4. Szablony tekstu będą działać w trybie eksperymentalnym podczas debugowania rozszerzenia DSL, a także będą działać na komputerze, na którym wdrożono rozszerzenie DSL.

Uwaga

Podczas tworzenia rozwiązania DSL w projekcie debugowania są generowane pliki *.tt przykładowego szablonu tekstowego *.tt . Po zmianie nazw klas domen te szablony nie będą już działać. Niemniej jednak obejmują one potrzebne podstawowe dyrektywy i podaj przykłady, które można zaktualizować, aby dopasować je do dsl.

Aby uzyskać dostęp do modelu z szablonu tekstowego:

  • Ustaw właściwość inherit dyrektywy szablonu na Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation. Zapewnia to dostęp do Sklepu.

  • Określ procesory dyrektyw dla rozszerzenia DSL, do których chcesz uzyskać dostęp. Spowoduje to załadowanie zestawów dla rozszerzenia DSL, aby można było używać jej klas domen, właściwości i relacji w kodzie szablonu tekstowego. Ładuje on również określony plik modelu.

    Plik .tt podobny do poniższego przykładu jest tworzony w projekcie Debugowanie podczas tworzenia nowego rozwiązania programu Visual Studio na podstawie szablonu DSL Minimal Language.

<#@ 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 #>
<#
  }
#>

Zwróć uwagę na następujące kwestie dotyczące tego szablonu:

  • Szablon może używać klas domen, właściwości i relacji zdefiniowanych w definicji DSL.

  • Szablon ładuje plik modelu określony we requires właściwości .

  • Właściwość w pliku this zawiera element główny. Z tego miejsca kod może przechodzić do innych elementów modelu. Nazwa właściwości jest zwykle taka sama jak główna klasa domeny w domenie DSL. W tym przykładzie jest to this.ExampleModel.

  • Mimo że język, w którym pisane są fragmenty kodu, to C#, można wygenerować tekst dowolnego rodzaju. Możesz też napisać kod w języku Visual Basic, dodając właściwość language="VB" do template dyrektywy.

  • Aby debugować szablon, dodaj debug="true" go do template dyrektywy. Jeśli wystąpi wyjątek, szablon zostanie otwarty w innym wystąpieniu programu Visual Studio. Jeśli chcesz podzielić debuger w określonym punkcie kodu, wstaw instrukcję System.Diagnostics.Debugger.Break();

    Aby uzyskać więcej informacji, zobacz Debugowanie szablonu tekstowego T4.

Informacje o procesorze dyrektywy DSL

Szablon może używać klas domen zdefiniowanych w definicji DSL. Jest to spowodowane dyrektywą, która zwykle pojawia się na początku szablonu. W poprzednim przykładzie jest to następujące polecenie.

<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1'" #>

Nazwa dyrektywy ( MyLanguagew tym przykładzie) pochodzi od nazwy DSL. Wywołuje procesor dyrektywy, który jest generowany jako część DSL. Kod źródłowy można znaleźć w pliku Dsl\GeneratedCode\DirectiveProcessor.cs.

Procesor dyrektywy DSL wykonuje dwa główne zadania:

  • Skutecznie wstawia dyrektywy zestawów i importu do szablonu, który odwołuje się do rozszerzenia DSL. Dzięki temu można używać klas domen w kodzie szablonu.

  • Ładuje on plik określony w parametrze requires i ustawia właściwość, która this odwołuje się do elementu głównego załadowanego modelu.

Weryfikowanie modelu przed uruchomieniem szablonu

Przed wykonaniem szablonu można spowodować zweryfikowanie modelu.

<#@ MyLanguage processor="MyLanguageDirectiveProcessor" requires="fileName='Sample.myDsl1';validation='open|load|save|menu'" #>

Zwróć uwagę, że:

  1. Parametry filename i validation są oddzielone znakiem ";" i nie muszą istnieć żadne inne separatory ani spacje.

  2. Lista kategorii weryfikacji określa, które metody weryfikacji zostaną wykonane. Wiele kategorii powinno być rozdzielonych znakiem "|" i nie może istnieć żadne inne separatory ani spacje.

    Jeśli zostanie znaleziony błąd, zostanie zgłoszony w oknie błędów, a plik wynikowy będzie zawierać komunikat o błędzie.

Uzyskiwanie dostępu do wielu modeli z szablonu tekstowego

Uwaga

Ta metoda umożliwia odczytywanie wielu modeli w tym samym szablonie, ale nie obsługuje odwołań modelu ModelBus. Aby odczytać modele połączone za pomocą odwołań modelu ModelBus, zobacz Using Visual Studio ModelBus in a Text Template (Używanie modelu programu Visual Studio ModelBus w szablonie tekstowym).

Jeśli chcesz uzyskać dostęp do więcej niż jednego modelu z tego samego szablonu tekstowego, należy wywołać wygenerowany procesor dyrektywy jednorazowo dla każdego modelu. Należy określić nazwę pliku każdego modelu w parametrze requires . Należy określić nazwy, które mają być używane dla głównej klasy domeny w parametrze provides . Należy określić różne wartości parametrów provides w każdym wywołaniu dyrektywy. Załóżmy na przykład, że masz trzy pliki modelu o nazwie Library.xyz, School.xyz i Work.xyz. Aby uzyskać dostęp do nich z tego samego szablonu tekstowego, należy napisać trzy wywołania dyrektywy podobne do poniższych.

<#@ 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" #>

Uwaga

Ten przykładowy kod jest przeznaczony dla języka opartego na szablonie rozwiązania Minimal Language.

Aby uzyskać dostęp do modeli w szablonie tekstowym, możesz teraz napisać kod podobny do kodu w poniższym przykładzie.

<#
foreach (ExampleElement element in this.LibraryModel.Elements)
...
foreach (ExampleElement element in this.SchoolModel.Elements)
...
foreach (ExampleElement element in this.WorkModel.Elements)
...
#>

Dynamiczne ładowanie modeli

Jeśli chcesz określić w czasie wykonywania, które modele mają być ładowane, możesz dynamicznie załadować plik modelu w kodzie programu, zamiast używać dyrektywy specyficznej dla języka DSL.

Jednak jedną z funkcji dyrektywy specyficznej dla dsL jest zaimportowanie przestrzeni nazw DSL, aby kod szablonu mógł używać klas domen zdefiniowanych w tym DSL. Ponieważ nie używasz dyrektywy, musisz dodać dyrektywy zestawu> i <importu> dla wszystkich modeli, które mogą być ładowane.< Jest to łatwe, jeśli różne modele, które można załadować, to wszystkie wystąpienia tego samego rozszerzenia DSL.

Aby załadować plik, najbardziej efektywną metodą jest użycie programu Visual Studio ModelBus. W typowym scenariuszu szablon tekstowy będzie używać dyrektywy specyficznej dla języka DSL, aby załadować pierwszy model w zwykły sposób. Ten model zawiera odwołania modelu ModelBus do innego modelu. Możesz użyć modelu ModelBus, aby otworzyć przywołany model i uzyskać dostęp do określonego elementu. Aby uzyskać więcej informacji, zobacz Using Visual Studio ModelBus in a Text Template (Używanie klasy ModelBus programu Visual Studio w szablonie tekstowym).

W mniej typowym scenariuszu możesz otworzyć plik modelu, dla którego masz tylko nazwę pliku i który może nie znajdować się w bieżącym projekcie programu Visual Studio. W takim przypadku można otworzyć plik przy użyciu techniki opisanej w temacie Jak otworzyć model z pliku w kodzie programu.

Generowanie wielu plików na podstawie szablonu

Jeśli chcesz wygenerować kilka plików — na przykład w celu wygenerowania oddzielnego pliku dla każdego elementu w modelu istnieje kilka możliwych metod. Domyślnie tylko jeden plik jest generowany z każdego pliku szablonu.

Dzielenie długiego pliku

W tej metodzie użyjesz szablonu do wygenerowania pojedynczego pliku oddzielonego ogranicznikiem. Następnie podzielisz plik na jego części. Istnieją dwa szablony, jeden do wygenerowania pojedynczego pliku, a drugi do podzielenia.

PętlaTemplate.t4 generuje długi pojedynczy plik. Zwróć uwagę, że jego rozszerzenie pliku to ".t4", ponieważ nie powinno być przetwarzane bezpośrednio po kliknięciu przycisku Przekształć wszystkie szablony. Ten szablon przyjmuje parametr, który określa ciąg ogranicznika, który oddziela segmenty:

<#@ 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.ttLoopTemplate.t4wywołuje metodę , a następnie dzieli wynikowy plik na jego segmenty. Zwróć uwagę, że ten szablon nie musi być szablonem modelowania, ponieważ nie odczytuje modelu.

<#@ 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]);
  }
#>