Understanding T4: <#@ assembly #> directive


Here is how MSDN documentation defines the assembly directive.

The assembly directive identifies an assembly to be referenced so that you can use types within that assembly from code in the text template. Using the assembly directive is equivalent to using the Add Reference feature in Visual Studio.

This directive does not affect the source code of the compiled template. Instead it affects compiler options T4 uses to compile GeneratedTextTransformation into a .NET assembly. Here is an example of a T4 template and C# compiler T4 uses to compile it.

Template

<#@ template language=C#debug=True#>
<#@ assembly name=System.Data#>
<#@ assembly name=System.Xml#>
<#@ import namespace=System.Data#>
<#
    DataSet dataSet = new DataSet();
#>

Compiler Options

/t:library /utf8output /D:DEBUG /debug+ /optimize- /w:4
/R:"...\System.dll"
/R:"...\System.Data.dll"
/R:"...\System.Xml.dll"
/R:"...\Microsoft.VisualStudio.TextTemplating.VSHost.dll"
/R:"...\Microsoft.VisualStudio.TextTemplating.dll"
/out:"...\qcckmauz.dll"
"...\qcckmauz.0.cs"

As you can see, a template with two assembly directives is compiled with references to the assemblies they specify. This allows you to use code from any .NET assemblies in your template.

How T4 resolves assembly names

Name property of the assembly directive can contain either a rooted file path or an assembly name.

Rooted File Path

If the name property contains a rooted file path (path that includes drive letter, such as “C:\MyAssembly.dll”), T4 will go to the file directly. Here is an example of a template that uses this option and command line options T4 generates to compile this template.

Template 

<#@ template language=C##>
<#@ assembly name=C:\ClassLibrary1\bin\Debug\ClassLibrary1.dll#>


Compiler Options

/t:library /utf8output /D:DEBUG /debug+ /optimize- /w:4
/R:"C:\ClassLibrary1\bin\Debug\ClassLibrary1.dll"
/R:"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll"
/R:"C:\WINDOWS\assembly\...\Microsoft.VisualStudio.TextTemplating.VSHost.dll"
/R:"C:\WINDOWS\assembly\...\Microsoft.VisualStudio.TextTemplating.dll"
/out:"C:\Documents and Settings\...\Local Settings\Temp\2jniyhqf.dll"
"C:\Documents and Settings\...\Local Settings\Temp\2jniyhqf.0.cs"

Main disadvantage of this approach is that it requires using absolute file paths, making it difficult to use in team environment where different developers have different folder structures on their workstations.

Global Assembly Cache

If the name property does not contain a rooted file path, T4 will first try to find the assembly in the GAC. Assembly name in this case can be a strong name with assembly version, culture and public key token information. In the example above, T4 located System.Data assembly in the global assembly cache.

Project Properties

If the assembly cannot be found in the global assembly cache, T4 will try to find it using properties of the project to which the T4 template itself belongs. It will first try to find the assembly among assemblies referenced by the project. If assembly still cannot be found, it will then search in the folders specified in project Reference Paths.

Project Reference Assembly Reference Paths

Here’s a modified version of the template, which can be used in either one of these two projects:

<#@ template language=C#debug=True#>
<#@ assembly name=ClassLibrary1.dll#>

T4 generates the same command line options to compile this template as the one in previous example, which relies on rooted file path. Compared to rooted file paths, using project properties allows to avoid hard-coding absolute file paths to the assemblies because project references use relative paths by default. The main drawback of this approach is that it will work only when template is transformed by T4 custom tool in Visual Studio. It will not work when template is transformed by the TextTransform.exe (T4 command line host) which doesn’t know if T4 template belongs to a project.

Assembly Locking

In order to improve performance, T4 engine reuses templating AppDomain for multiple template transformation, which causes all referenced assembly files to be locked. This presents a problem if you are using your own assembly during code generation, because the assembly file cannot be recompiled until the file is unlocked by T4. You can force T4 to unload the file by closing and reopening the Visual Studio solution or by restarting Visual Studio itself. Alternatively, consider using VolatileAssembly custom directive available in T4 Toolbox, which creates a shadow copy of the assembly before loading it.

About T4

T4 (Text Template Transformation Toolkit) is a template-based code generation engine. It is available in Visual Studio 2008 and as a download in DSL and GAT toolkits for Visual Studio 2005. T4 engine allows you to use ASP.NET-like template syntax to generate C#, T-SQL, XML or any other text files.

Ready for more? Read about <#@ include #> directive or go back to the overview.


Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Added description of how T4 resolves assembly name.

My T4 template doesnt compile unless I add an assembly reference to “System.dll”.
I’m confused at this because in your example there’s no such reference. Also, your assembly references don’t include the .dll suffix, but if I remove the .dll suffix from System.dll, I get a different error: “Template1.cs(1,1): error CS0116: A namespace does not directly contain members such as fields or methods”.
Can you explain what’s going on in this situation?

Unfortunately, I cannot explain what is going on based on this informaiton. Could you provide more details, such as example of your template code, which host you are using to transform it (visual studio, command line), etc?

Once VS/T4 loads the referenced assembly, it never unloads it. If I want to recompile the referenced assembly, I get an error. I end up restarting VS a lot. Annoying.

VERSIONS IN ASSEMBLY DIRECTIVE
(I hope the following might be useful for somebody)

You can declare the assembly version to use in T4 by declaring it inside the name’s string, like the following example:

assembly name=”Microsoft.SqlServer.Smo, Version=10.0.0.0″

I had to figure out this when I got the following error today:

error BC32206: Compiling transformation: The project currently contains references to more than one version of Microsoft.SqlServer.Smo, a direct reference to version 9.0.242.0 and an indirect reference (through ‘CustomProyectDLL’) to version 10.0.0.0. Change the direct reference to use version 10.0.0.0 (or higher) of Microsoft.SqlServer.Smo.

Same complains as Kevin
Once VS/T4 loads the referenced assembly, it never unloads it. If I want to recompile the referenced assembly, I get an error. I end up restarting VS a lot. Annoying…

This didn’t happend versions before…
I use this particular Template to Transform some xml in json using xsl. No big deal. The template is located in a ASP WebApp and the static type/method that transforms the xml are located in a separate dll. The same dll is referenced by the web Application. As i said this wasn’t the case with older versions of the t4toolkit

ok the answer was allready here… downloading fxCop…

The locking behavior is a side-effect of T4 engine reusing the templating AppDomain to improve performance. You can use VolatileAssembly custom directive from T4 Toolbox to get around this problem. I have updated the article to include links to details.

Hi Oleg,

Great source of info, but some of it appears to be out of date with respect to vs 2010.
This post clarifies what to do and the options available.
http://weblogs.asp.net/lhunt/archive/2010/05/04/t4-template-error-assembly-directive-cannot-locate-referenced-assembly-in-visual-studio-2010-project.aspx

Time for an update?