I. Introduction▲
Le Template T4 est une technologie de génération de code, existant depuis Visual Studio 2005. Développée par Microsoft, cette technologie peut servir dans de nombreuses situations.
Le fonctionnement est simple : on écrit un Template (code) qui permettra de générer des fichiers, du code… Cette technologie est très flexible et on pourra l'adapter à tous les problèmes.
Enfin, sachez qu'aucune installation supplémentaire n'est requise si vous disposez de Visual Studio 2008 ou 2010.
II. Outils▲
Comme nous l'avons vu dans l'introduction, aucun outil supplémentaire n'est nécessaire à l'utilisation des Templates T4. Néanmoins, l'intégration par défaut laisse à désirer :
- pas de coloration syntaxique ;
- pas d'autocomplétion.
Je vais donc vous présenter une liste d'outils qui vont nous permettre de travailler dans de meilleures conditions. Je n'ai pas la prétention de connaître tous les outils existants. Si vous pensez repérer un manque, faites-le-moi parvenir et je me ferai une joie de l'intégrer à cet article.
II-A. T4 Toolbox▲
La T4 Toolbox est un peu le couteau suisse du Template T4 ! Elle apporte de nombreuses fonctionnalités qui pourront vous aider à la création de Templates T4 avancés. Une de ces fonctionnalités primordiales est à mon sens la possibilité de générer plusieurs fichiers depuis un seul Template T4.
Vous pouvez télécharger la T4 Toolbox ici : http://t4toolbox.codeplex.com/T4 Toolbox.
II-B. Tangible T4 Editor▲
Tangible T4 Editor est un éditeur pour Templates T4. Il apporte principalement la coloration syntaxique, mais aussi l'autocomplétion. Il fournit aussi une galerie de Templates, très utile pour éviter de réinventer la roue.
Attention : Tangible T4 Editor est un logiciel payant. Une version gratuite est proposée, mais vous devrez faire face à quelques limitations : autocomplétion limitée à certaines assemblies, modélisation UML limitée. Pour le téléchargement, ça se passe ici : Tangible T4 Editor.
II-C. Visual T4▲
Visual T4 est un plugin pour Visual Studio qui apporte la coloration syntaxique, mais aussi l'autocomplétion. À la différence de Tangible T4 Editor, il est entièrement gratuit. Je vous conseille de comparer les deux et de prendre celui sur lequel vous êtes le plus à l'aise.
Le téléchargement se passe ici : Visual T4.
III. Les Templates T4 en détail▲
Avant toute chose, nous allons nous intéresser à la syntaxe des Templates T4. Nous partirons sur un cas pratique par la suite, qui nous permettra d'assimiler ces informations.
III-A. Syntaxe▲
Les Templates T4 disposent d'une syntaxe qui leur est propre. Cette syntaxe ressemble à celle de l'ASP, et est donc facile à assimiler.
La première chose à savoir est que tout texte saisi hors balise se retrouvera au sein du fichier de sortie. Prenons l'exemple du Template T4 suivant :
<
#@ template debug=
"true"
hostspecific=
"false"
language=
"C#"
#>
<
#@ output extension=
".txt"
encoding=
"UTF8"
#>
Hello World !
Le résultat sera un simple fichier texte comprenant « Hello World ! ».
Un bloc de code s'insère de cette manière :
<
#@ template debug=
"true"
hostspecific=
"false"
language=
"C#"
#>
<
#@ output extension=
".txt"
encoding=
"UTF8"
#>
<
#
String message =
"Hello World !"
;
#>
Pour afficher une variable ou du texte issu d'un traitement :
<
#@ template debug=
"true"
hostspecific=
"false"
language=
"C#"
#>
<
#@ output extension=
".txt"
encoding=
"UTF8"
#>
<
#
String message =
"Hello World !"
;
#>
<
#=
message #>
Il existe un dernier type de bloc dans les Templates T4 : les class feature blocks. Ces blocs permettent de déclarer des classes et des méthodes utilisables dans votre Template T4. Ces blocs doivent toujours être déclarés en fin de votre Template T4 et s'utilisent de la manière suivante :
<
#@ template debug=
"true"
hostspecific=
"false"
language=
"C#"
#>
<
#@ output extension=
".txt"
encoding=
"UTF8"
#>
<
#=
ToUpper
(
"Hello World !"
) #>
<
#+
public
string
ToUpper
(
String message)
{
return
message.
ToUpper
(
);
}
#>
Pour faire un petit résumé, voici les différentes balises des Templates T4 :
- : directive ;
- : bloc de code ;
- : affichage de texte depuis du code ;
- : class feature blocks.
III-B. Les directives▲
Les directives sont des informations à placer obligatoirement au début de votre Template T4. Elles permettent de spécifier le comportement de celui-ci. Elles se composent toutes de la même syntaxe :
<
#@ DirectiveName [
Attribute =
"Value"
]
#>
III-B-1. Template▲
La directive Template est la directive principale d'un Template T4. Elle va permettre de spécifier les caractéristiques générales de notre Template T4 :
- Language [string] : spécifie le langage du Template T4 (VB, C#) ;
- Debug [bool] : active ou désactive le débogage sur le Template ;
- HostSpecific [bool] : donne accès ou non à l'objet Host, fournissant un ensemble d'informations ;
- Inherits [string] : spécifie la classe de génération dont hérite le Template T4. Cet attribut est facultatif et a pour valeur par défaut TextTransformation.
III-B-2. Output▲
Quand on utilise un Template T4, le code généré va se retrouver dans un fichier de sortie. Ce fichier porte le même nom que celui du Template T4. Pour le visionner, il suffit de déplier le fichier .tt dans l'explorateur de solutions.
Nous avons aussi la possibilité de spécifier l'extension du fichier de sortie. Pour cela, il suffit de modifier cette ligne :
<
#@ output extension=
".txt"
#>
Un Template T4 ne génère par défaut qu'un unique fichier de sortie. Un article à venir décrira comment générer plusieurs fichiers de sortie.
Nous allons aussi pouvoir spécifier l'encodage du fichier de sortie. Pour cela, on va ajouter l'attribut encoding :
<
#@ output extension=
".txt"
encoding=
"UTF8"
#>
III-B-3. Assembly▲
Lorsque l'on crée un Template T4, celui-ci ne possède pas les références de notre projet. Nous allons donc, dans certains cas, lui ajouter des références. Pour cela, nous disposons de la directive Assembly :
<
#@ assembly name=
"System.Data"
#>
On peut noter que certaines « assemblies » sont incluses par défaut dans un Template T4 :
- System ;
- Microsoft.VisualStudio.TextTemplating.VSHost ;
- Microsoft.VisualStudio.TextTemplating.dll ;
- mscorlib.
III-B-4. Import▲
Dans notre Template T4, nous ne pouvons pas utiliser les usings. Nous allons donc utiliser la directive Import, qui répond à la même problématique. Celle-ci s'utilise de cette manière :
<
#@ import namespace
=
"System.Data.SqlClient"
#>
III-B-5. Include▲
Au sein de notre Template T4, nous pouvons aussi inclure d'autres Templates T4, grâce à la directive Include. Les chemins peuvent être relatifs ou absolus :
<
#@ include file=
"c:\Projects
\T
4\include1.tt"
#>
<
#@ include file=
"..
\T
4\include1.tt"
#>
III-C. Exécution▲
Maintenant que nous avons créé notre Template T4, nous allons l'exécuter pour lancer la génération de notre fichier de sortie. Plusieurs options s'offrent à nous :
- sauvegarder le fichier .tt : cette action lance automatiquement l'exécution du Template T4 ;
- clic droit sur notre fichier .tt : exécuter un outil personnalisé ;
- utiliser le bouton spécifique dans l'explorateur de solution (lance l'exécution de tous les Templates T4) : .
IV. Un peu de pratique▲
Maintenant que nous avons vu la théorie, nous allons passer à la pratique. Nous allons créer un Template T4 qui va parcourir un dossier contenant des classes, et pour chacune d'elles, générer une propriété dans notre fichier de sortie. L'intérêt du Template en lui-même est limité, mais il nous permettra d'appréhender la création d'un Template T4.
IV-A. Création de la solution▲
Notre solution va être simple : un projet de type « bibliothèque de classes », comprenant un dossier « classes » et notre Template T4 :
IV-B. Création du Template T4▲
Dans un premier temps, nous allons modifier le type de sortie de notre Template T4 en fichier .cs, mais aussi importer le namespace System.IO :
<
#@ template debug=
"false"
hostspecific=
"true"
language=
"C#"
#>
<
#@ output extension=
".cs"
#>
<
#@ import namespace
=
"System.IO"
#>
Une fois cette étape réalisée, nous allons créer des variables utiles au fonctionnement de notre Template T4 :
string
NameClass =
"ClassT4"
;
//nom de la classe générée par le template
string
NameSpace =
"T4Practice"
;
//Namespace de la classe générée
string
folder =
"Classes"
;
//Dossier contenant nos classes
DirectoryInfo directoryTemplate =
new
FileInfo
(
Host.
TemplateFile).
Directory;
//répertoire contenant notre tt
Afin de rendre plus lisible le code de notre Template, nous allons le segmenter en plusieurs méthodes. La première sera chargée d'écrire les différents usings :
private
void
writeUsings
(
string
space,
string
folder)
{
#>
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Text;
using
<
#=
space +
"."
+
folder #>;
<
#+
}
La dernière ligne sert à créer le using pour pouvoir disposer de nos classes. La prochaine fonction aura pour rôle d'écrire le namespace et la déclaration de la classe :
private
void
BeginHeader
(
string
space,
string
nameClass)
{
#>
namespace
<
#=
space #>
{
public
class
<
#=
nameClass #>
{
<
#+
}
Cette fonction attend en paramètres le namespace et le nom de la classe. La troisième fonction servira à fermer les accolades à la fin de notre fichier :
private
void
EndNamespace
(
)
{
#>
}
}
<
#+
}
Enfin, notre dernière fonction servira à écrire le code relatif à une propriété :
private
void
WriteProperty
(
string
name)
{
#>
public
<
#=
String.
Format
(
"{0} {1}"
,
name,
name.
ToLower
(
)) #>
{
get
;
set
;
}
<
#+
}
Grâce à ces quatre méthodes, nous allons pouvoir générer notre Template T4. Nous allons maintenant ajouter le code permettant de parcourir les fichiers concernés. Pour commencer, déclarons quelques variables :
string
NameClass =
"ClassT4"
;
string
NameSpace =
"T4Practice"
;
string
folder =
"Classes"
;
DirectoryInfo directoryTemplate =
new
FileInfo
(
Host.
TemplateFile).
Directory;
- NameClass : nom de la classe générée par notre Template T4 ;
- NameSpace : espace de nom dans lequel doit se situer notre classe générée ;
- folder : nom du dossier contenant nos classes ;
- directoryTemplate : répertoire parent de notre Template.
Maintenant, il ne nous reste plus qu'à créer le code pour remplir notre fichier de sortie :
writeUsings
(
NameSpace,
folder);
BeginHeader
(
NameSpace,
NameClass);
foreach
(
FileInfo file in
new
DirectoryInfo
(
directoryTemplate.
FullName +
@"\"
+
folder).
GetFiles
(
))
{
WriteProperty
(
file.
Name.
Split
(
'.'
).
GetValue
(
0
).
ToString
(
));
}
EndNamespace
(
);
- On insère les différents usings ;
- On insère le namespace et la déclaration de la classe ;
- Pour chaque fichier du dossier, on va créer une propriété ;
- On termine en fermant les différentes accolades.
IV-C. Résultat final▲
Nous en avons donc fini avec notre Template. Voici le résultat obtenu en entier :
<
#@ template debug=
"false"
hostspecific=
"true"
language=
"C#"
#>
<
#@ output extension=
".cs"
#>
<
#@ import namespace
=
"System.IO"
#>
<
#
string
NameClass =
"ClassT4"
;
string
NameSpace =
"T4Practice"
;
string
folder =
"Classes"
;
DirectoryInfo directoryTemplate =
new
FileInfo
(
Host.
TemplateFile).
Directory;
writeUsings
(
NameSpace,
folder);
BeginHeader
(
NameSpace,
NameClass);
foreach
(
FileInfo file in
new
DirectoryInfo
(
directoryTemplate.
FullName +
@"\"
+
folder).
GetFiles
(
))
{
WriteProperty
(
file.
Name.
Split
(
'.'
).
GetValue
(
0
).
ToString
(
));
}
EndNamespace
(
);
#>
<
#+
private
void
writeUsings
(
string
space,
string
folder)
{
#>
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Text;
using
<
#=
space +
"."
+
folder #>;
<
#+
}
private
void
BeginHeader
(
string
space,
string
nameClass)
{
#>
namespace
<
#=
space #>
{
public
class
<
#=
nameClass #>
{
<
#+
}
private
void
EndNamespace
(
)
{
#>
}
}
<
#+
}
private
void
WriteProperty
(
string
name)
{
#>
public
<
#=
String.
Format
(
"{0} {1}"
,
name,
name.
ToLower
(
)) #>
{
get
;
set
;
}
<
#+
}
#>
Lorsque nous exécutons notre Template, nous obtenons le code suivant (en fonction des différentes classes créées) :
using
System;
using
System.
Collections.
Generic;
using
System.
Linq;
using
System.
Text;
using
T4Practice.
Classes;
namespace
T4Practice
{
public
class
ClassT4
{
public
Class1 class1 {
get
;
set
;
}
public
Class2 class2 {
get
;
set
;
}
}
}
V. Conclusion▲
À travers cet article, nous avons découvert les Templates T4. Comme vous avez pu le voir, cette technologie va nous permettre de gagner un temps considérable dans nos développements. Si l'exemple donné semble simple, sachez que vous pouvez adapter les Templates T4 à de nombreuses situations. Je vous invite donc à lire mes autres articles (Templates T4 et Entity FrameworkTemplates T4 et Entity Framework, par Kevin Perriat, Classes Métadonnées, Entity Framework et Templates T4Classes Métadonnées, Entity Framework et Templates T4, par Kevin Perriat) pour plus d'informations.
VI. Remerciements▲
Je tiens à remercier Paul Duguet pour sa participation à la rédaction de cet article, ainsi que toute l'équipe Développez. Je remercie aussi jacques_jean et djibril pour leurs corrections, ainsi que Jean-Michel Ormes pour ses nombreux conseils.