Error when generating T4 template

Aug 18, 2011 at 5:48 PM

Hi, I'm trying to generate multiple output files in my solution using some of the tutorials I've read online.  I'm running into a curious error which seems to be coming from the T4 Toolbox itself.  Here is the error I'm getting:

An Exception was thrown while running the transformation code. The process cannot continue.  The following Exception was thrown:
System.NullReferenceException: Object reference not set to an instance of an object.
   at T4Toolbox.DteProcessor.UpdateFiles(ICollection`1 outputFiles)
   at T4Toolbox.OutputManager.UpdateFiles()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at T4Toolbox.TransformationContext.OnTransformationEnded(TextTransformation transformation)
   at Microsoft.VisualStudio.TextTemplatingE5BF8E73A8B09AC4832B923CE9B77784.GeneratedTextTransformation.Dispose(Boolean disposing) in c:\TFSPRD2010\FMG_Collection\RiskConsole\DEV\Antioch\RiskConsoleCodedUI\RiskConsole.CodedUI.Persistence\EnumTemplates\EnumMasterTemplate.tt:line 71
   at Microsoft.VisualStudio.TextTemplating.TextTransformation.Dispose()
   at Microsoft.VisualStudio.TextTemplating.TransformationRunner.RunTransformation(TemplateProcessingSession session, String source, ITextTemplatingEngineHost host, String& result) 

and here is my code:

<#@ template debug="true" hostSpecific="true" language="C#" #>

<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>

<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>

<#
//Debugger.Launch();
//Debugger.Break();

string xmlConfigPath = this.Host.ResolvePath("EnumMasterConfig.xml");
XmlDocument xmlConfig = new XmlDocument();
TextTemplatingSession session = new TextTemplatingSession();

xmlConfig.Load(xmlConfigPath);
FetchSessionVariables(xmlConfig, session);

ITextTemplatingSessionHost host = (ITextTemplatingSessionHost)this.Host;
host.Session = session;
Engine engine = new Engine();

string templateContentPath = this.Host.ResolvePath(host.Session["TemplateContentPath"].ToString());
string templateContent = File.ReadAllText(templateContentPath);

engine.ProcessTemplate(templateContent, this.Host);
#>
<#+ 
public IDictionary<string, string> GetXmlVariables(XmlDocument xmlDoc)
{
	IDictionary<string, string> xmlVariables = new Dictionary<string, string>();
	
	foreach (XmlNode node in xmlDoc.SelectNodes("/enumMasterTemplate/variables/add"))
		xmlVariables.Add(node.Attributes["key"].Value, node.Attributes["value"].Value);
	
	return xmlVariables;
}

public void FetchSessionVariables(XmlDocument xmlDoc, TextTemplatingSession session)
{
	IDictionary<string, string> variables = GetXmlVariables(xmlDoc);
	string outValue;
	string innerText;
	
	foreach (XmlNode node in xmlDoc.SelectNodes("/enumMasterTemplate/properties/property"))
	{
		if (node.InnerText.Trim().StartsWith("$(") && node.InnerText.Trim().EndsWith(")"))
		{
			innerText = node.InnerText.Replace("$(", String.Empty).Replace(")", String.Empty);
			session[node.Attributes["name"].Value] = variables.TryGetValue(innerText, out outValue) ? variables[innerText] : String.Empty;
		}
		else
			session[node.Attributes["name"].Value] = node.InnerText;
	}
}
#>

Any help would be appreciated.  Thanks.

-John 

Aug 18, 2011 at 8:25 PM

Using .NET Reflector, I've been able to step through the T4 Toolbox code and I've found the culprit of my Null Reference Exception in the T4Toolbox.DteProcessor.UpdateFiles(ICollection`1 outputFiles) method.  On line 340, the DirectiveProcessor.Host.get_TemplateFile() method is called, but the DirectiveProcessor.Host property is null, hence the Null Reference exception.  My question is, any idea why this value is null, and is there any way I can supply a valid value to this property in my template to avoid this exception at runtime?

UpdateFiles method from DteProcessor.cs (From T4Toolbox.10.0.dll) where I am getting the exception:

internal static void UpdateFiles(System.Collections.Generic.ICollection<OutputFile> outputFiles)
    {
      string str = System.Environment.get_CurrentDirectory();
      System.Environment.set_CurrentDirectory(System.IO.Path.GetDirectoryName(DirectiveProcessor.Host.get_TemplateFile()));
      try
      {
        Solution solution = GetDte().Solution;
        System.Collections.Generic.IEnumerable<EnvDTE.Project> allProjects = GetAllProjects(solution);
        ProjectItem template = solution.FindProjectItem(DirectiveProcessor.Host.get_TemplateFile());
        Validate(outputFiles, allProjects);
        CreateLogIfNecessary(outputFiles, template);
        DeleteOldOutputs(outputFiles, solution, template);
        UpdateOutputFiles(outputFiles, solution, allProjects, template);
      }
      finally
      {
        System.Environment.set_CurrentDirectory(str);
      }
    }
Thanks, 
John
Aug 18, 2011 at 10:50 PM

I found the problem, it seems I was missing the <#@ include file="T4Toolbox.tt" #> directive in one of my template files, lol.