T4 Toolbox - Referencing Class in Current Assembly
Asked Answered
C

4

15

I am writing a T4 script which reflects over certain classes and provides code generation based on them. The problem is that my script errors out, saying that the classes in my current project cannot be accessed.

The script itself resides in the same assembly as the classes I am trying to reference. I've tried referencing the namespace, the file and adding a reference to the current assembly (the project itself) - all to no avail.

What am I missing?

Climax answered 21/7, 2010 at 10:8 Comment(0)
U
10

I believe this is what Nicko and uosɐſ are looking for. Just change the "MyAssembly.CodeGeneration" to the name of the project with the T4 templates.

<#@ assembly name="$(TargetPath)MyAssembly.dll" #>
<#@ import namespace="MyAssembly.CodeGeneration" #>
Unreasonable answered 20/10, 2012 at 19:38 Comment(2)
I used $(TargetPath) instead. It is the macro for the dll. <#@ assembly name="$(TargetPath)" #> <#@ import namespace="MyAssembly.CodeGeneration" #>Bussell
It would of been nice if you left the original solution as wellSigmatism
E
2

One thing to keep in mind is that the T4 script you are writing doesn't actually "reside in the same assembly" - because it is design-time code, not run-time code. In other words - it doesn't get compiled into your assembly at all.

Instead, the T4 template script is run while you are writing code - or, if you have certain hooks enabled, whenever you build/compile your program. Because it's actually separate from your project, code, though, it has no ability to reference your project's assembly directly - unless you use something like DTE - which gives you the ability to access the Visual Studio environment itself, and explore elements such as the currently-loaded project.

As an example, consider the following script:

<#@ template language="C#" debug="false" hostspecific="true" #>
<#@ output extension=".js" #>
<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="T4Toolbox.tt" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>

string targetNamespace = "MyNamespace";
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);

var classes = FindClasses(project, targetNamespace, "");

<# foreach (CodeClass c in classes) { #>

    public class <#= c.Name #> {

<#     var properties = c.Members.OfType<EnvDTE.CodeProperty>()
           .Where(p => p.Access.HasFlag(vsCMAccess.vsCMAccessPublic))
           .OrderBy(p => p.Name);
       foreach (var prop in properties) { 
#>

       public <#= prop.Type.AsString #> <#= prop.Name #> { get; set; }

<#     } #>

   }

<# } #>

<#+ List<CodeClass> FindClasses(Project project, string ns, string className) {
        List<CodeClass> result = new List<CodeClass>();
        FindClasses(project.CodeModel.CodeElements, className, ns, result, false);
        return result;
    }
    void FindClasses(CodeElements elements, string className, string searchNamespace, List<CodeClass> result, bool isNamespaceOk) {
        if (elements == null) return;
        foreach (CodeElement element in elements) {
            if (element is CodeNamespace) {
                CodeNamespace ns = element as CodeNamespace;
                if (ns != null) {
                    if (ns.FullName == searchNamespace)
                        FindClasses(ns.Members, className, searchNamespace, result, true);
                    else
                        FindClasses(ns.Members, className, searchNamespace, result, false);
                }
            } else if (element is CodeClass && isNamespaceOk) {
                CodeClass c = element as CodeClass;
                if (c != null) {
                    if (c.FullName.Contains(className))
                        result.Add(c);

                    FindClasses(c.Members, className, searchNamespace, result, true);
                }
            }
        }
    }

This script, in essence, will run through a specific namespace (in this case "MyNamespace"), iterate through all of the classes therein, then output a new code file which lists only their public properties with a getter/setter - in essence, producing a POCO of the objects. In some of my projects, I use an adapted version of this code to generate JavaScript objects based on my POCOs, so that my JS models can always be in-sync with my server-side objects, from a serialization perspective.

The trick to it, though, is in the first few lines:

var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);
var classes = FindClasses(project, targetNamespace, "");

In essence, the DTE service is asking Visual Studio to give it an abstract model of the currently-loaded Solution and it's Projects. We then load up the Project in which the current TemplateFile is stored, and in the FindClasses() method, parse out the classes within that project which match our search criteria.

I hope the example code gives you a starting point to jump off from - but if you need further detail, here are a few additional references for you to sink your teeth into:

Engorge answered 22/4, 2013 at 21:25 Comment(0)
S
1

For some reason I couldn't get @brian solution working. I ending up doing this In my case T4Generators was my seperate project in the same solution.

<#@ assembly name="$(SolutionDir)\T4Generators\bin\Debug\T4Generators.dll" #>
<#@ import Namespace="T4Generators" #>
Sigmatism answered 25/3, 2017 at 1:14 Comment(0)
B
0

Reference it common way. Then check if assembly is loaded, if not - generate stub code (to make compilation possible; after compilation run T4 again, to genereate real code). And have unit test that could prevent stub code going to production.

Boraginaceous answered 20/9, 2015 at 9:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.