How do I pass a string parameter to a t4 template
Asked Answered
N

3

18

Hi I am trying to find a way to pass a normal string as a parameter to a text template.

This is my Template code, if someone could tell me what I would need to write in c# to pass my parameters and create the class file. That would be very helpful, Thanks.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ parameter name="namespacename" type="System.String" #>
<#@ parameter name="classname" type="System.String" #>
<#
this.OutputInfo.File(this.classname);
#>
namespace <#= this.namespacename #>
{
    using System;
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Xml; 

    /// <summary>
    /// This class describes the data layer related to <#= this.classname #>.
    /// </summary>
    /// <history>
    ///   <change author=`Auto Generated` date=<#= DateTime.Now.ToString("dd/MM/yyyy") #>>Original Version</change>
    /// </history>
    public partial class <#= this.classname #> : DataObject
    {
        #region constructor

        /// <summary>
        /// A constructor which allows the base constructor to attempt to extract the connection string from the config file.
        /// </summary>
        public <#= this.classname #>() : base() {}

        /// <summary>
        /// A constructor which delegates to the base constructor to enable use of connection string.
        /// </summary>
        /// <param name='connectionstring`></param>
        public <#= this.classname #>(string connectionstring) : base(connectionstring) {}

        #endregion
    }
}
Neckwear answered 11/4, 2013 at 10:47 Comment(0)
X
16

The following is one way to pass parameters:

  1. You have to create TextTemplatingSession.
  2. Set the session dictionary for the parameters.
  3. Process the template using that session.

Sample code (Replace the ResolvePath with the location of your tt file):

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<# 
string templateFile = this.Host.ResolvePath("ClassGeneration.tt");
string templateContent = File.ReadAllText(templateFile);

TextTemplatingSession session = new TextTemplatingSession();
session["namespacename"] = "MyNamespace1";
session["classname"] = "MyClassName";

var sessionHost = (ITextTemplatingSessionHost) this.Host;
sessionHost.Session = session;

Engine engine = new Engine();
string generatedContent = engine.ProcessTemplate(templateContent, this.Host);

this.Write(generatedContent);  #>

I saw this example on Oleg Sych's blog, which is great resource for t4. Here is the updated link: https://web.archive.org/web/20160706191316/http://www.olegsych.com/2010/05/t4-parameter-directive

Xantho answered 11/4, 2013 at 20:31 Comment(2)
The link seems to be brokenPhilanthropy
He moved his website here is the link from wayback machine: web.archive.org/web/20160706191316/http://www.olegsych.com/2010/…Xantho
A
15

This is a 3-year old question and I don't know how much the templating libraries have evolved and if my solution to the problem applies to older versions of Visual Studio and/or .NET etc. I'm currently using Visual Studio 2015 and .NET 4.6.1.

Summary

Use a "Class feature control block" to declare public members to the generated class of your template and refer to these public members in your template text.

Example

In a C# project select Add New Item > Runtime Text Template > "Salutation.tt". You get a new .tt file with the following default declarations:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>

Below the declarations enter your template text:

My name is <#= Name #>.
<# if (RevealAge) { #>
I am <#= Age #> years old.
<# } #>

At the end of the .tt file add your parameters as public class members inside a "Class feature control block". This block must go to the end of the file.

<#+
public string Name { get; set; }
public int Age { get; set; }
public bool RevealAge = false;
#>

Then, for example, in a Console Application, you can use your template as follows:

Console.Write(new Salutation
{
    Name = "Alice",
    Age = 35,
    RevealAge = false
}.TransformText());

Console.Write(new Salutation
{
    Name = "Bob",
    Age = 38,
    RevealAge = true
}.TransformText());

And get the following output:

My name is Alice.
My name is Bob.
I am 38 years old.
Press any key to continue . . .

For more information regarding the T4 syntax, see MSDN article Writing a T4 Text Template.

Arguello answered 1/11, 2016 at 11:11 Comment(1)
@Rice, it still works for me. Just tried the exact steps of my example in a new console project in VS2017. New Project > Visual C# > Windows Classic Desktop > Console App (.NET Framework) using .NET 4.7.1. The T4 is still supported. Perhaps you were trying in a .NET Core project. I don't think T4 is supported in Core. But I personally have switched to dotliquid for the past few months now and I am very happy with it. You should try it! And it works with Core as well.Arguello
H
1

It is also possible to inject parameters to template content before processing it. To do it, add <##> to your template as a placeholder for code injection.

GenericClass.ttinclude:

<#@ template language="C#" #>
<##>
namespace <#= Namespace #>
{
    public class <#= ClassName #> : IGeneric<<#= TypeParameter #>>
    {
        public <#= TypeParameter #> ReturnResult() => 1 + 3;
    }
}
<#+
    public string ClassName { get; set; }
    public string Namespace { get; set; }
    public string TypeParameter { get; set; }
#>

Than add a common template for importing other templates (Generator.ttinclude):

<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#+
    public const string ParametersInjectionPattern = "<" + "#" + "#" + ">";

    public void Generate(string baseTemplatePath, IDictionary<string, string> parameters)
    {
        var template = File.ReadAllText(this.Host.ResolvePath(baseTemplatePath));

        template = template.Replace(ParametersInjectionPattern, GenerateParametersAssignmentControlBlock(parameters));

        this.Write(new Engine().ProcessTemplate(template, this.Host).Trim());
    }

    public string GenerateParametersAssignmentControlBlock(IDictionary<string, string> parameters)
    {
        var sb = new StringBuilder();
        sb.Append('<').Append('#').Append(' ');

        foreach (var parameter in parameters)
            sb.Append($"{parameter.Key} = {parameter.Value};");

        sb.Append(' ').Append('#').Append('>');
        return sb.ToString();
    }

    public string SurroundWithQuotes(string str)
    {
        return $"\"{str}\"";
    }

    public string GetTemplateName()
    {
        return Path.GetFileNameWithoutExtension(this.Host.TemplateFile).Replace(".Generated", "");
    }
#>

And then use it in any specific template, where you can just pass the necessary parameters (UInt64Class.Generated.tt):

<#@ template hostspecific="true" language="C#" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ include file="Generator.ttinclude" #>
<#
    var parameters = new Dictionary<string, string>()
    {
        { "Namespace", SurroundWithQuotes("T4GenericsExample") },
        { "ClassName", SurroundWithQuotes(GetTemplateName()) },
        { "TypeParameter", SurroundWithQuotes("System.UInt64") }
    };

    Generate("GenericClass.ttinclude", parameters);
#>

The complete example can be found at https://github.com/Konard/T4GenericsExample

Holston answered 15/5, 2019 at 8:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.