Подтвердить что ты не робот

T4 Toolbox - ссылочный класс в сборке

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

Следует иметь в виду, что T4 script, который вы пишете, на самом деле не "находится в одной сборке", потому что это код времени разработки, а не код времени исполнения. Другими словами - он вообще не компилируется в вашу сборку.

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

В качестве примера рассмотрим следующий 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);
                }
            }
        }
    }

Этот script, по сути, будет проходить через определенное пространство имен (в данном случае "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() анализируем классы внутри этого проекта, которые соответствуют нашим критериям поиска.

Надеюсь, что примерный код дает вам отправную точку для перехода от него, но если вам нужна дополнительная информация, вот несколько дополнительных ссылок для того, чтобы вы погрузили свои зубы в:

Ответ 3

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

Ответ 4

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

<#@ assembly name="$(SolutionDir)\T4Generators\bin\Debug\T4Generators.dll" #>
<#@ import Namespace="T4Generators" #>