Création de modèles T4 à l'exécution (build-time)?

Nous construisons une application interne qui doit générer des fichiers HTML pour les télécharger dans les listes eBay. Nous cherchons à utiliser un moteur de modèle pour générer les fichiers HTML basés sur la base de données et les champs statiques que nous avons prédéfinis. Le modèle doit également avoir des capacités logiques (si-alors, foreach, etc).

Nous avons regardé T4 et il semble parfait, mais nous ne voyons rien sur le fait qu'il ait les capacités à utiliser lors de l'exécution, de sorte qu'un utilisateur puisse créer le T4 modèle, puis l'application peut "compiler" et générer le fichier HTML final. Est-ce possible, et comment?

Sinon, y a-t-il d'autres cadres que nous devrions regarder qui ont toutes ces capacités?

21
demandé sur Amberite 2010-02-22 00:40:52

7 réponses

J'ai un ensemble similaire de classes que j'utilise pour cela, en intégrant la génération de texte modélisée dans le logiciel.

Fondamentalement, cela fonctionne comme ASP de style ancien, vous entourez le code C # dans les blocs <%...%>, et vous pouvez émettre des résultats en utilisant <%= expression %>.

Vous pouvez passer un seul objet dans le code du modèle, qui peut bien sûr être n'importe quel type d'objet que vous aimez, ou simplement un tableau de paramètres. Vous pouvez également référencer vos propres assemblys si vous souhaitez exécuter du code personnalisé.

Voici comment émettre une classe ressemblerait à:

<%
var parameters = (string[])data;
var namespaceName = parameters[0];
var className = parameters[1];
%>
namespace <%= namespaceName %>
{
    public class <%= className %>
    {
    }
}

Vous pouvez bien sûr faire une boucle à travers les choses:

<% foreach (var parameter in parameters) { %>
<%= parameter %>
<% } %>

Et mettre du code dans if-blocks etc.

La bibliothèque de classes est disponible sur CodePlex ici:

Ainsi que sur NuGet .

Le projet est livré avec des exemples, téléchargez la source ou parcourez-la en ligne .

Pour répondre aux questions par email aussi ici, pour les autres à voir:

  1. tous les types de code C# qui correspondent à un appel de méthode peuvent être compilés dans le modèle. Il exécute du code C# 3.5 normal, avec tout ce qui signifie, Il n'y a pas de limites artificielles. Seules les choses à savoir est que tout si, tandis que, pour, foreach, etc. le code qui contient le code de modèle à émettre doit utiliser des accolades, vous ne pouvez pas faire un bloc de type if-then à une seule ligne. Voir ci-dessous pour la limitation de l'appel de méthode.
  2. le paramètre data correspond à tout ce qui a été passé paramètre de la méthode .Generate(x) de votre application, et est du même type. Si vous transmettez un objet que vous avez défini dans vos propres bibliothèques de classes, vous devez ajouter une référence au Code du modèle afin d'y accéder correctement. (<%@ reference your.class.library.dll %>)
  3. Si vous réutilisez le modèle compilé, il ne s'agira essentiellement que d'un appel de méthode à une classe, aucune surcharge supplémentaire n'est effectuée sur l'appel réel à .Generate(). Si vous n'appelez pas .Compile() vous-même, le premier appel à .Generate() prendra soin d'elle. Notez également que le code s'exécute dans un appdomain séparé, donc il y a une légère surcharge de marshalling liée à la copie du paramètre et du résultat d'avant en arrière. Le code, cependant, fonctionne à la vitesse de code. net JITted normale.

Exemple de bloc if:

<% if (a == b) { %>
This will only be output if a==b.
<% } %>

Il n'y a pas non plus de limites artificielles sur le formatage du code, choisissez le style qui vous convient le mieux:

<%
    if (a == b)
    {
%>
This will only be output if a==b.
<%
    }
%>

Notez seulement que toutes les parties non codées du modèle seront à peu près sorties telles quelles, ce qui signifie que les onglets et autres éléments suivants %> les blocs seront également sortis.

Il y a une limite, tout le code que vous écrivez doit tenir dans un seul appel de méthode.

Laissez-moi vous expliquer.

La façon dont le moteur de modèle fonctionne est qu'il produit un .fichier cs et le nourrit au compilateur C#, ceci .le fichier cs ressemble grossièrement à ceci:

using directives

namespace SomeNamespace
{
    public class SomeClass
    {
        public string Render(object data)
        {
            ... all your code goes here
        }
    }
}

Cela signifie que vous ne pouvez pas définir de nouvelles classes, de nouvelles méthodes, de champs de niveau de classe, etc.

Vous pouvez toutefois utiliser des délégués anonymes pour créer des fonctions interne. Par exemple, si vous voulez une manière uniforme de formater les dates:

Func<DateTime, string> date2str = delegate(DateTime dt)
{
    return dt.ToString("G");
};

Ensuite, vous pouvez simplement l'utiliser dans le reste du code du modèle:

<%= date2str(DateTime.Now) %>

La seule exigence que j'ai est que vous ne téléchargez pas les fichiers sur le web et que vous prétendez avoir écrit le code, à part que vous êtes libre de faire ce que vous voulez avec.

Edit 23.04.2011: liens fixes vers le projet CodePlex.

16
répondu Lasse Vågsæther Karlsen 2011-04-23 19:39:01

Si vous pouvez utiliser Visual Studio 2010 pour la création et l'édition de modèles, vous pouvez utiliser des modèles précompilés, qui ont été conçus pour exactement ce scénario et sont l'option prise en charge par Microsoft.

Vous concevez le modèle dans Visual Studio, le précompilez et déployez un assembly qui n'a aucune dépendance sur Visual Studio avec votre application.

Http://www.olegsych.com/2009/09/t4-preprocessed-text-templates/

14
répondu GarethJ 2010-05-18 02:33:56

L'assembly qui implémente la transformation de texte T4 est Microsoft.VisualStudio.TextTemplating.dll, qui est livré avec Visual Studio.

Si vous voulez commencer à partir des premiers principes, vous devez implémenter Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost , et transmettez votre implémentation en tant qu'argument à Microsoft.VisualStudio.TextTemplating.Moteur.ProcessTemplate () , qui effectuera la transformation.

Cela vous donne plus de flexibilité que d'appeler à TextTransform.EXE.

Cependant, si votre code est un produit d'expédition, il n'est pas clair quelle est la licence de cet assembly, et si vous avez les droits de le redistribuer avec votre application.

Redistribuer cet assembly éviterait la nécessité D'installer Visual Studio.

6
répondu Leon Breedt 2010-02-22 03:24:12

Les modèles T4 peuvent être compilés avec le TextTransform.exe outil de ligne de commande. Vous votre application pourrait créer un fichier. tt, puis appeler TextTransform.exe pour générer la sortie.

3
répondu CodingWithSpike 2010-02-21 21:48:08

Il est tout à fait possible D'utiliser T4 au moment de l'exécution.

Microsoft ne supporte pas vraiment ce scénario de manière raisonnable dans. net 3.5. Il semble que. NET 4.0 aura un meilleur support de Microsoft.

Mono fournit un certain support pour ce scénario dans. net 3.5.

J'ai prouvé ce concept avec succès avec. net 3.5 avec l'aide de L'implémentation Mono T4, mais une solution standard pour ce problème pour. Net 3.5 nécessiterait une tonne plus d'effort que moi ont investi jusqu'à présent.

Vous pouvez trouver L'implémentation Mono T4 ici:

Https://github.com/mono/monodevelop/tree/master/main/src/addins/TextTemplating

J'ai documenté certains des problèmes que j'ai rencontrés en essayant d'exécuter des modèles T4 à partir du code. net ici:

Options pour exécuter des modèles T4 à partir du code. net

3
répondu Michael Maddox 2011-05-21 21:01:58

Une erreur que j'ai faite est que j'ai ajouté un fichier "modèle de texte". Pour générer du texte à runtime , Choisissez le" modèle de texte prétraité " à la place. Si vous avez initialement choisi "modèle de texte", il est facile de définir l'outil personnalisé sur "TextTemplatingFilePreprocessor" dans les propriétés du fichier dans VS.

0
répondu Clay Lenhart 2012-11-27 14:56:14

Liquide pourrait être un bon choix pour cela. Ceci est un langage de modèle open-source, en savoir plus sur la langue ici: https://shopify.github.io/liquid/

Voici une implémentation pour. NET: https://github.com/dotliquid/dotliquid

La syntaxe est plutôt sympa. Voici un exemple de code pour C#:

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public List<string> Friends { get; set; }
    }

    static void Main(string[] args)
    {
        Template.RegisterSafeType(typeof(Person), new string[]
            {
                nameof(Person.Name),
                nameof(Person.Age),
                nameof(Person.Friends),
            });

        Template template = Template.Parse(
@"<h1>hi {{name}}</h1> 
<p>You are{% if age > 42' %} old {% else %} young{% endif %}.</p>
<p>You have {{ friends.size }} friends:</p>
{% assign sortedfriends = friends | sort %}
{% for item in sortedfriends -%}
  {{ item | escape }} <br />
{% endfor %}

");
        string output = template.Render(
            Hash.FromAnonymousObject(
                new Person()
                {
                    Name = "James Bond",
                    Age = 42,
                    Friends = new List<string>()
                    {
                        "Charlie",
                        "<TagMan>",
                        "Bill"
                    }
                } ));

        Console.WriteLine(output);

/* The output will be: 

<h1>hi James Bond</h1>
<p>You are young.</p>
<p>You have 3 friends:</p>

  &lt;TagMan&gt; <br />
  Bill <br />
  Charlie <br />             

*/

    }
0
répondu PEK 2018-09-13 13:17:16