John Livermore
John Livermore

Reputation: 31333

T4 conditionally including templates

I am creating a T4 template that I wish to conditionally include templates based on some criteria. For example I have tried the following...

<#switch(iocContainer) {#>
<#case "Autofac":#>
    <#@ include file="Autofac\EntityTemplate.ttinclude" #>
    <#@ include file="Autofac\ServiceTemplate.ttinclude" #>
    <#@ include file="Autofac\RepositoryTemplate.ttinclude" #>
    <#@ include file="Autofac\DbContextTemplate.ttinclude" #>
<#break;#>

<#case "Castle":#>
    <#@ include file="Castle\EntityTemplate.ttinclude" #>
    <#@ include file="Castle\ServiceTemplate.ttinclude" #>
    <#@ include file="Castle\RepositoryTemplate.ttinclude" #>
    <#@ include file="Castle\DbContextTemplate.ttinclude" #>
<#break;#>

<#case "nInject":#>
    <#@ include file="nInject\EntityTemplate.ttinclude" #>
    <#@ include file="nInject\ServiceTemplate.ttinclude" #>
    <#@ include file="nInject\RepositoryTemplate.ttinclude" #>
    <#@ include file="nInject\DbContextTemplate.ttinclude" #>
<#break;
}#>

The problem is when the templating engine runs, it appears to preprocess all the includes before evaluating any code. So the switch statement above isn't running and T4 attempts to include all the files.

Is there a way to conditionally include T4 templates?

Upvotes: 1

Views: 2900

Answers (4)

David M
David M

Reputation: 72890

No, T4 doesn't include conditional includes by design. The directives will all be processed.

If you invert your logic you can solve this. Split the T4 content above and below your switch statement into two separate files (let's call them start.ttinclude and end.ttinclude). Then create three separate T4 templates called, say, autofac.tt, castle.tt and ninject.tt. These would each looks something like this (this is castle.tt):

<#@ include file="start.ttinclude" #>
<#@ include file="Castle\EntityTemplate.ttinclude" #>
<#@ include file="Castle\ServiceTemplate.ttinclude" #>
<#@ include file="Castle\RepositoryTemplate.ttinclude" #>
<#@ include file="Castle\DbContextTemplate.ttinclude" #>
<#@ include file="end.ttinclude" #>

Upvotes: 2

Karson
Karson

Reputation: 459

I wanted to include a different config file per developer machine and also to have a default app.config. This will only work when you don't want processing for the "included" files.

File name App.tt.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".config" #>
<#
string pathToConfigurations = Host.ResolvePath("Configurations");
string pathToMachine = Path.Combine(pathToConfigurations, Environment.MachineName + ".config");
if (File.Exists(pathToMachine))
{
    Write(File.ReadAllText(pathToMachine)); 
}
else
{
    Write(File.ReadAllText(Path.Combine(pathToConfigurations, "App.config")));  
}
#>

Upvotes: 0

Wolfgang Grinfeld
Wolfgang Grinfeld

Reputation: 1

If you don't mind running within a Visual Studio container, then DTE can help you out here.

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="EnvDTE" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Data" #>
<#@ Assembly Name="System.Xml" #>
<#@ Assembly Name="System.Xml.Linq" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System" #>
<#@ Import namespace="System.Linq"                    #>
<#@ Import namespace="System.Reflection"              #>
<#@ Import namespace="System.Linq.Expressions"        #>
<#@ Import namespace="System.Collections.Generic"     #>
<#@ Import namespace="System.Linq"                    #>
<#@ Import namespace="System.Text"                    #>
<# 
    if(isDefined("spike2" , "TTs")) {
        WriteLine("//the constant TTs is defined");
    }
    else
    {
        WriteLine("//the constant TTs is not defined");
    }
#>
<#+
        private bool isDefined(string projectName, string constantToCheck){
            IServiceProvider serviceProvider = (IServiceProvider)this.Host;
            DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;  

            try
            {   
                // find a project called "Spike2"
                Project prj = (from p in getProjects(dte) where p.Name.ToLower() == projectName.ToLower() select p).FirstOrDefault(); 

                // get its active configuration
                var config = prj.ConfigurationManager.ActiveConfiguration;


                var defineConstants = (from p in getConfigProperties(config) where p.Name == "DefineConstants" select p.Value).FirstOrDefault().ToString(); 
                var constants = defineConstants.Split(';');
                return Array.Exists(constants, define => define == constantToCheck);
            }
            catch(Exception ex)
            {
                Write(ex.Message);
            }
            return false;
        }

        static private IEnumerable<Property> getConfigProperties(Configuration config) {
            for(int i=1; i <=  config.Properties.Count; i++)
            {
                yield return  config.Properties.Item(i);
            }
        }

        static private IEnumerable<Project> getProjects(DTE dte) {
            for(int i=1; i <= dte.Solution.Projects.Count; i++)
            {
                yield return dte.Solution.Projects.Item(i);
            }
        }
#>

Upvotes: 0

GarethJ
GarethJ

Reputation: 6606

Alternatively, place the content of the template inside an #IF #ENDIF block and use #DEFINEs to cause the content to be skipped even though the file itself is included.

Upvotes: 1

Related Questions