Génération de texte durant l'exécution à l'aide des modèles de texte T4

Vous pouvez générer des chaînes de texte dans votre application au moment de l’exécution à l’aide de modèles de texte runtime Visual Studio. L’ordinateur sur lequel l’application s’exécute ne doit pas nécessairement avoir Visual Studio. Les modèles runtime sont parfois appelés « modèles de texte prétraités », car au moment de la compilation, le modèle génère du code qui est exécuté au moment de l’exécution.

Chaque modèle est un mélange du texte tel qu’il apparaîtra dans la chaîne générée et des fragments de code du programme. Les fragments de programme fournissent des valeurs pour les parties variables de la chaîne et contrôlent également les parties conditionnelles et répétées.

Par exemple, le modèle suivant peut être utilisé dans une application qui crée un rapport HTML.

<#@ template language="C#" #>
<html><body>
<h1>Sales for Previous Month</h2>
<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>
This report is Company Confidential.
</body></html>

Notez que le modèle est une page HTML dans laquelle les parties variables ont été remplacées par du code du programme. Vous pouvez commencer la conception d’une telle page en écrivant un prototype statique de la page HTML. Vous pouvez ensuite remplacer la table et d’autres parties variables par du code de programme qui génère le contenu qui varie d’une occasion à l’autre.

L’utilisation d’un modèle dans votre application permet de voir plus facilement la forme finale de la sortie que dans une longue série d’instructions d’écriture, par exemple. Il est plus facile et plus fiable d’apporter des modifications au formulaire de sortie.

Création d’un modèle de texte à l’exécution dans n’importe quelle application

Pour créer un modèle de texte à l’exécution

  1. Dans l’Explorateur de solutions, dans le menu contextuel de votre projet, choisissez Ajouter>Nouvel élément.

  2. Dans la boîte de dialogue Ajouter un nouvel élément, sélectionnez Modèle de texte runtime. (Dans Visual Basic, regardez sous Éléments communs>Général.)

  3. Tapez un nom pour votre fichier de modèle.

    Notes

    Le nom du fichier de modèle sera utilisé comme nom de classe dans le code généré. Par conséquent, il ne doit pas avoir d’espaces ou de ponctuation.

  4. Choisissez Ajouter.

    Un nouveau fichier est créé avec l’extension .tt. Sa propriété Outil de personnalisation est définie sur TextTemplatingFilePreprocessor. Elle contient les lignes suivantes :

    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    
  5. Ajoutez une référence au package NuGet System.CodeDom.

Conversion d’un fichier existant en modèle à l’exécution

Un bon moyen de créer un modèle consiste à convertir un exemple existant de la sortie. Par exemple, si votre application génère des fichiers HTML, vous pouvez commencer par créer un fichier HTML brut. Assurez-vous qu’il fonctionne correctement et que son apparence est correcte. Ensuite, incluez-le dans votre projet Visual Studio et convertissez-le en modèle.

Pour convertir un fichier existant en modèle à l’exécution

  1. Incluez le fichier dans votre projet Visual Studio. Dans l’Explorateur de solutions, dans le menu contextuel du projet, choisissez Ajouter>Élément existant.

  2. Définissez la propriété Outils de personnalisation du fichier sur TextTemplatingFilePreprocessor. Dans l’Explorateur de solutions, dans le menu contextuel du fichier, choisissez Propriétés.

    Notes

    Si la propriété est déjà définie, assurez-vous qu’elle est définie sur TextTemplatingFilePreprocessor et non TextTemplatingFileGenerator. Cela peut se produire si vous incluez un fichier qui a déjà l’extension .tt.

  3. Remplacez l’extension de nom de fichier par .tt. Bien que cette étape soit facultative, elle vous permet d’éviter d’ouvrir le fichier dans un éditeur incorrect.

  4. Supprimez les espaces ou la ponctuation de la partie principale du nom de fichier. Par exemple, « My Web Page.tt » est incorrect, mais « MyWebPage.tt » est correct. Le nom de fichier sera utilisé comme nom de classe dans le code généré.

  5. Insérez la ligne suivante au début du fichier. Si vous travaillez dans un projet Visual Basic, remplacez « C# » par « VB ».

    <#@ template language="C#" #>

  6. Ajoutez une référence au package NuGet System.CodeDom.

Contenu du modèle à l’exécution

Directive de modèle

Conservez la première ligne du modèle telle qu’elle était lorsque vous avez créé le fichier :

<#@ template language="C#" #>

Le paramètre de langage dépend du langage de votre projet.

Contenu brut

Modifiez le fichier .tt pour qu’il contienne le texte que votre application doit générer. Par exemple :

<html><body>
<h1>Sales for January</h2>
<!-- table to be inserted here -->
This report is Company Confidential.
</body></html>

Code de programme incorporé

Vous pouvez insérer du code de programme entre <# et #>. Par exemple :

<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>

Notez que les instructions sont insérées entre <# ... #> et les expressions sont insérées entre <#= ... #>. Pour plus d'informations, consultez Écriture d’un modèle de texte T4.

Utilisation du modèle

Code généré à partir du modèle

Lorsque vous enregistrez le fichier .tt, une filiale .cs ou un fichier .vb est généré(e). Pour voir ce fichier dans l’Explorateur de solutions, développez le nœud de fichier .tt. Dans un projet Visual Basic, cliquez d’abord sur Afficher tous les fichiers dans la barre d’outils de l’Explorateur de solutions.

Notez que le fichier filiale contient une classe partielle qui contient une méthode appelée TransformText(). Vous pouvez appeler cette méthode à partir de votre application.

Génération de texte à l’exécution

Dans le code de votre application, vous pouvez générer le contenu de votre modèle à l’aide d’un appel, comme suit :

MyWebPage page = new MyWebPage();
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Pour placer la classe générée dans un espace de noms particulier, définissez la propriété Espace de noms de l’outil de personnalisation du fichier de modèle de texte.

Débogage des modèles de texte runtime

Déboguez et testez des modèles de texte runtime de la même manière que le code ordinaire.

Vous pouvez définir un point d’arrêt dans un modèle de texte. Si vous démarrez l’application en mode débogage à partir de Visual Studio, vous pouvez parcourir le code et évaluer les expressions espion de la manière habituelle.

Passage de paramètres dans le constructeur

En règle générale, un modèle doit importer des données à partir d’autres parties de l’application. Pour faciliter cette tâche, le code généré par le modèle est une classe partielle. Vous pouvez créer une autre partie de la même classe dans un autre fichier de votre projet. Ce fichier peut inclure un constructeur avec des paramètres, des propriétés et des fonctions accessibles à la fois par le code incorporé dans le modèle et par le reste de l’application.

Par exemple, vous pouvez créer un fichier distinct MyWebPageCode.cs :

partial class MyWebPage
{
    private MyData m_data;
    public MyWebPage(MyData data) { this.m_data = data; }}

Dans votre fichier de modèle MyWebPage.tt, vous pouvez écrire :

<h2>Sales figures</h2>
<table>
<# foreach (MyDataItem item in m_data.Items)
   // m_data is declared in MyWebPageCode.cs
   { #>
      <tr><td> <#= item.Name #> </td>
          <td> <#= item.Value #> </td></tr>
<# } // end of foreach
#>
</table>

Pour utiliser ce modèle dans l’application :

MyData data = ...;
MyWebPage page = new MyWebPage(data);
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Paramètres du constructeur dans Visual Basic

Dans Visual Basic, le fichier distinct MyWebPageCode.vb contient :

Namespace My.Templates
  Partial Public Class MyWebPage
    Private m_data As MyData
    Public Sub New(ByVal data As MyData)
      m_data = data
    End Sub
  End Class
End Namespace

Le fichier de modèle peut contenir :

<#@ template language="VB" #>
<html><body>
<h1>Sales for January</h2>
<table>
<#
    For Each item In m_data.Items
#>
    <tr><td>Test name <#= item.Name #> </td>
      <td>Test value <#= item.Value #> </td></tr>
<#
    Next
#>
</table>
This report is Company Confidential.
</body></html>

Le modèle peut être appelé en passant le paramètre dans le constructeur :

Dim data = New My.Templates.MyData
    ' Add data values here ....
Dim page = New My.Templates.MyWebPage(data)
Dim pageContent = page.TransformText()
System.IO.File.WriteAllText("outputPage.html", pageContent)

Passage de données dans les propriétés du modèle

Une autre façon de passer des données dans le modèle consiste à ajouter des propriétés publiques à la classe de modèle dans une définition de classe partielle. Votre application peut définir les propriétés avant d’appeler TransformText().

Vous pouvez également ajouter des champs à votre classe de modèle dans une définition partielle. Cela vous permet de passer des données entre les exécutions successives du modèle.

Utiliser des classes partielles pour le code

De nombreux développeurs préfèrent éviter d’écrire des corps de code volumineux dans des modèles. Au lieu de cela, vous pouvez définir des méthodes dans une classe partielle portant le même nom que le fichier de modèle. Appelez ces méthodes à partir du modèle. De cette façon, le modèle montre plus clairement à quoi ressemblera la chaîne de sortie cible. Les discussions sur l’apparence du résultat peuvent être séparées de la logique de création des données qu’il affiche.

Assemblies et références

Si vous souhaitez que votre code de modèle référence un assembly .NET ou un autre assembly, tel que System.Xml.dll, ajoutez-le aux références de votre projet de la manière habituelle.

Si vous souhaitez importer un espace de noms de la même manière qu’une instruction using, vous pouvez le faire avec la directive import :

<#@ import namespace="System.Xml" #>

Ces directives doivent être placées au début du fichier, immédiatement après la directive <#@template.

Contenu partagé

Si vous avez du texte partagé entre plusieurs modèles, vous pouvez le placer dans un fichier distinct et l’inclure dans chaque fichier dans lequel il doit apparaître :

<#@include file="CommonHeader.txt" #>

Le contenu inclus peut contenir n’importe quel mélange de code de programme et de texte brut, ainsi que des directives d’inclusion et d’autres directives.

La directive d’inclusion peut être utilisée n’importe où dans le texte d’un fichier de modèle ou d’un fichier inclus.

Héritage entre modèles de texte à l’exécution

Vous pouvez partager du contenu entre des modèles à l’exécution en écrivant un modèle de classe de base, qui peut être abstrait. Utilisez le paramètre inherits de la directive <@#template#> pour référencer une autre classe de modèle runtime.

Modèle d’héritage : fragments dans les méthodes de base

Dans le modèle utilisé dans l’exemple suivant, notez les points suivants :

  • La classe de base SharedFragments définit des méthodes dans les blocs de fonctionnalités de la classe <#+ ... #>.

  • La classe de base ne contient pas de texte libre. Au lieu de cela, tous ses blocs de texte se produisent à l’intérieur des méthodes de fonctionnalités de la classe.

  • La classe dérivée appelle les méthodes définies dans SharedFragments.

  • L’application appelle la méthode TextTransform() de la classe dérivée, mais ne transforme pas la classe de base SharedFragments.

  • Les classes de base et dérivées sont des modèles de texte runtime. Autrement dit, la propriété Outil de personnalisation est définie sur TextTemplatingFilePreprocessor.

SharedFragments.tt :

<#@ template language="C#" #>
<#+
protected void SharedText(int n)
{
#>
   Shared Text <#= n #>
<#+
}
// Insert more methods here if required.
#>

MyTextTemplate1.tt :

<#@ template language="C#" inherits="SharedFragments" #>
begin 1
   <# SharedText(2); #>
end 1

MyProgram.cs :

...
MyTextTemplate1 t1  = new MyTextTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

La sortie résultante :

begin 1
    Shared Text 2
end 1

Modèle d’héritage : texte dans le corps de base

Dans cette autre approche de l’utilisation de l’héritage de modèle, la majeure partie du texte est définie dans le modèle de base. Les modèles dérivés fournissent des données et des fragments de texte qui s’intègrent dans le contenu de base.

AbstractBaseTemplate1.tt :

<#@ template language="C#" #>

Here is the description for this derived template:
  <#= this.Description #>

Here is the fragment specific to this derived template:
<#
  this.PushIndent("  ");
  SpecificFragment(42);
  this.PopIndent();
#>
End of common template.
<#+
  // State set by derived class before calling TextTransform:
  protected string Description = "";
  // 'abstract' method to be defined in derived classes:
  protected virtual void SpecificFragment(int n) { }
#>

DerivedTemplate1.tt :

<#@ template language="C#" inherits="AbstractBaseTemplate1" #>
<#
  // Set the base template properties:
  base.Description = "Description for this derived class";

  // Run the base template:
  base.TransformText();

#>
End material for DerivedTemplate1.

<#+
// Provide a fragment specific to this derived template:

protected override void SpecificFragment(int n)
{
#>
   Specific to DerivedTemplate1 : <#= n #>
<#+
}
#>

Code de l’application :

...
DerivedTemplate1 t1 = new DerivedTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Résultat :

Here is the description for this derived template:
  Description for this derived class

Here is the fragment specific to this derived template:
     Specific to DerivedTemplate1 : 42
End of common template.
End material for DerivedTemplate1.

Modèles au moment de la conception : si vous souhaitez utiliser un modèle pour générer du code qui fait partie de votre application, consultez Génération de code au moment de la conception à l’aide de modèles de texte T4.

Les modèles à l’exécution peuvent être utilisés dans n’importe quelle application où les modèles et leur contenu sont déterminés au moment de la compilation. Toutefois, si vous souhaitez écrire une extension Visual Studio qui génère du texte à partir de modèles qui changent au moment de l’exécution, consultez Appel de la transformation de texte dans une extension VS.