2010-07-21 2 views
12

Я пишу сценарий T4, который отражает определенные классы и обеспечивает создание кода на их основе. Проблема в том, что мой скрипт ошибочно, говоря, что классы в моем текущем проекте недоступны.T4 Toolbox - ссылочный класс в текущей сборке

Сам сценарий находится в той же сборке, что и классы, которые я пытаюсь использовать. Я пробовал ссылаться на пространство имен, файл и добавлять ссылку на текущую сборку (сам проект) - все это безрезультатно.

Что мне не хватает?

ответ

9

Я считаю, что это то, что ищут Nicko и uosɐs. Просто измените «MyAssembly.CodeGeneration» на название проекта с помощью шаблонов T4.

<#@ assembly name="$(TargetPath)MyAssembly.dll" #> 
<#@ import namespace="MyAssembly.CodeGeneration" #> 
+8

Вместо этого я использовал $ (TargetPath). Это макрос для dll. <# @ assembly name = "$ (TargetPath)" #> <# @ import namespace = "MyAssembly.CodeGeneration" #> –

+0

Было бы неплохо, если бы вы также оставили исходное решение –

2

Одна вещь, чтобы иметь в виду, что T4 скрипт вы пишете на самом деле не «находятся в той же сборке» - потому что во время разработки кода, а не во время выполнения кода. Другими словами - он не скомпилируется в вашу сборку вообще.

Вместо этого скрипт шаблона T4 запускается во время написания кода - или, если у вас есть определенные перехватчики, всякий раз, когда вы создаете/компилируете свою программу. Поскольку на самом деле это из вашего проекта, код, он не имеет возможности напрямую ссылаться на сборку вашего проекта - если вы не используете что-то вроде DTE, что дает вам возможность доступа к самой среде Visual Studio и изучить элементы таких как загруженный в настоящий момент проект.

В качестве примера рассмотрим следующий сценарий:

<#@ 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); 
       } 
      } 
     } 
    } 

Этот сценарий, по сути, будет выполняться через определенный имен (в данном случае "MyNamespace"), перебирать все классы в ней, а затем выводить новый файл кода, в котором перечислены только их общедоступные свойства с getter/setter - по существу, создавая POCO объектов. В некоторых моих проектах я использую адаптированную версию этого кода для генерации объектов JavaScript на основе моих POCOs, так что мои JS-модели всегда могут быть синхронизированы с моими серверными объектами с точки зрения сериализации.

Хитрость к нему, хотя, в первые несколько строк:

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

В сущности, служба DTE просит Visual Studio, чтобы придать ему абстрактную модель текущей загруженной Solution и это Projects , Затем мы загружаем Project, в котором хранится текущий TemplateFile, и в методе FindClasses() проанализируйте классы внутри этого проекта, которые соответствуют нашим критериям поиска.

Я надеюсь, что пример кода дает вам отправную точку, чтобы спрыгнуть, - но если вам нужно более подробно, вот несколько дополнительных ссылок для вас, чтобы погрузить Ваши зубы в:

0

Reference это распространенный способ.Затем проверьте, загружена ли сборка, если нет - сгенерируйте код-заглушку (чтобы сделать компиляцию возможной, после компиляции запустите T4 снова, чтобы генерировать реальный код). И у вас есть единичный тест, который может помешать созданию кода-заглушки.

0

По какой-то причине я не смог получить решение @brian. Я заканчиваю это делать В моем случае T4Generator был моим отдельным проектом в том же решении.

<#@ assembly name="$(SolutionDir)\T4Generators\bin\Debug\T4Generators.dll" #> 
<#@ import Namespace="T4Generators" #> 
Смежные вопросы