Reputation: 33078
I'm working on my first T4 code generation tool to add some Stored Procedure helper code to my project. I've created custom types (e.g. StoredProcedure
and StoredProcedureParameter
to help with my code generation and have included the assembly and namespace references in my code:
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".generated.vb" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="StoredProcCodeGenerator" #>
This allows me to use my custom types in my T4 template code. However, because my custom types exist in the same project as the T4 template code, I can't recompile my project once I run the template code without restarting Visual Studio. This isn't very much fun.
I read a great article that addresses this exact issue by using the T4 Toolbox, but it's not working. Either I'm implementing the VolatileAssembly
directive wrong or the T4 toolbox simply didn't get installed. I'm not sure that the toolbox got installed correctly (I'm using VS 2010 on Win XP).
What are some ways that I might be able to fix this problem?
Upvotes: 23
Views: 9144
Reputation: 18746
You can also walk the code using EnvDte:
<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="cs" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #><#
var serviceProvider = Host as IServiceProvider;
if (serviceProvider != null) {
Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
}
// Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
if (Dte == null) {
throw new Exception("T4Generator can only execute through the Visual Studio host");
}
Project = GetProjectContainingT4File(Dte);
if (Project == null) {
Error("Could not find the VS Project containing the T4 file.");
return"XX";
}
AppRoot = Path.GetDirectoryName(Project.FullName) + '\\';
RootNamespace = Project.Properties.Item("RootNamespace").Value.ToString();
Console.WriteLine("Starting processing");
ProcessFileCodeModel(Project);
#>
I've posted even more code using this as a basis at http://imaginarydevelopment.blogspot.com/2010/11/static-reflection-or-t4-with-envdte.html
Upvotes: 1
Reputation: 18746
Hopefully this is helpful, it shows a use case of consuming the volatileAssembly, I'm not having any luck getting this t4 template to work at all, but I think it may be helpful:
// <autogenerated/>
// Last generated <#= DateTime.Now #>
<#@ template language="C#" hostspecific="true"#>
<#@ assembly name="System" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\FrameworkWpf.dll" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\FrameworkTestToolkit.dll" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="bin\debug\WpfAppTemplate.exe" #>
<#@ output extension=".cs" #>
<#@ import namespace="System" #>
<#@ import namespace="FrameworkTestToolkit" #>
namespace WpfAppTemplateTest {
using System;
using System.Reflection;
<#
// Add new types into the below array:
Type[] types = new Type[] {
typeof(FrameworkWpf.SafeEvent),
typeof(FrameworkWpf.Mvvm.ControllerBase),
typeof(FrameworkTestToolkit.PrivateAccessorGeneratorTestClass),
typeof(WpfAppTemplate.PostController),
typeof(WpfAppTemplate.ShellController),
};
// Do not modify this code
foreach (Type type in types) {
PrivateAccessorGenerator builder = new PrivateAccessorGenerator(type, WriteLine, Error, Warning);
builder.Generate();
}
#>
}
from http://blog.rees.biz/Home/unit-testing-and-private-accessors2
Upvotes: 1
Reputation: 33078
You need to remove the previous assembly
reference and then add the VolatileAssembly
reference. If you don't remove the regular assembly
reference first, you'll get an error that it has already been added when you add the VolatileAssembly
reference.
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".generated.vb" #>
<#@ assembly name="$(TargetPath)" #>
<#@ VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor"
name="$(TargetPath)" #>
<#@ import namespace="StoredProcCodeGenerator" #>
Now you can continue to build your project and use types defined in that project within your T4 templates.
Upvotes: 10