T4 - TT - Использование пользовательских классов в файлах TT

Я хотел бы использовать свой собственный класс определения в файле CS в моем TT.

Пример:

public class ClassDefinition
{
    public string NameSpace { get; set; }
    public string Name { get; set; }
    public string Protection { get; set; }

    List<ClassProperty> Properties { get; set; }
}

Мой ТТ выглядит так:

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>

<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Xml"#>

<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>

<#@ include file="$(ProjectDir)ClassDefinition.cs" #>

<#

// Read the model file
XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(this.Host.ResolvePath("GeneratedXmlFile.xml"));

IList<XmlNode> nodeList = new List<XmlNode>();
foreach (XmlNode node in doc.DocumentElement)
{
    switch(node.Name)
    {
        case "Model": 
        {
            ClassDefinition classDefinition = new ClassDefinition();

Но у меня есть это сообщение об ошибке:

Преобразование компиляции: не удалось найти тип или имя пространства имен ClassDefinition (отсутствует директива using или ссылка на сборку?)

Я проверил в Интернете и попытался: - использовать включить - использовать сборку - использовать ИСПОЛЬЗОВАНИЕ Но ничего не работает.

Есть идеи?

4 ответа

Решение

Вот полное решение:

1) Разделить классы в другой проект. 2) Включить ссылку на эти классы через ТТ через

<#@ assembly name="$(TargetDir)MyOwnLibraryProject.dll" #>
<#@ import namespace="MyOwnNamespace" #>

3) Не забудьте включить ссылку на эту библиотеку в ваш проект ТТ

4) Вы должны скопировать MyOwnLibraryProject.dll в папку BIN\DEBUG решения TT.

5) Волшебство появляется!!!

Каждый раз, когда вы меняете свою DLL, не забывайте помещать новую версию в папку:) Или просто настройте выходные данные проекта библиотеки так, чтобы они совпадали с выходом TT. Я хотел бы поблагодарить вас всех за руководство и идеи.

Если я вас правильно понимаю, вы пытаетесь повторно использовать класс как часть генерации шаблона.

Этот класс должен быть в самом файле tt, действие сборки не установлено, пользовательский инструмент - ничего. У меня есть класс менеджера шаблонов со следующим:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Diagnostics" #>

<#+
public class TemplateManager
{

Затем в других шаблонах t4 я использую:

<#@ include file="TemplateManager.tt"#>

а потом

List<Values> values = TemplateManager.PrepareVariables(code, container, itemCollection.OfType<EntityType>())

В вашем случае этот файл ClassDefinition.tt будет содержать:

<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Diagnostics" #>

<#+
public class ClassDefinition
{
    public string NameSpace { get; set; }
    public string Name { get; set; }
    public string Protection { get; set; }

    List<ClassProperty> Properties { get; set; }
}
#>

Тогда вы можете включить

<#@ include file="ClassDefinition.tt"#>

У меня была такая же проблема - мое решение было похоже на @Tehseen, за исключением того, что я предоставлю реальное решение с объяснением:)

Уловка для включения произвольного C# в файл T4 (где вы хотите, чтобы импортированный C# использовался T4, а не просто включался как необработанный текст), заключается в том, чтобы скрыть части файла *.cs, которые T4 будет подавлять - например, using директивы и гарантируя, что типы объявлены в <#+ (вместо того <#) блок.

Вот мое решение:

Моя "точка входа" MyScript.tt Скрипт T4 выглядит так:

<#@ template debug="true" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ include file="IncludedCSFile.cs" #>
<#@ output extension=".cs" #>
<#

MyClass foo = new MyClass(); // This is using a type from `IncludedCSFile.cs` in the T4 script.

#>

Hello, <#= foo.Name #>

Мой IncludedCSFile.cs выглядит так:

// <#+ /*

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace MyNamespace
{

// */

    public class MyClass
    {
        // etc...
    }
}
// #>

Пояснение:

// <#+ /*
  • Начальный // останавливает основной синтаксический анализатор C# (из проекта) от просмотра T4 <#+ разделитель, который может вызвать синтаксическую ошибку проекта.
  • В <#+Однако это анализируется Т4 анализатором и вызывает C# в файле должны интерпретироваться как код, который Т4 сценарий можно использовать.
  • В /* следующий запускает новый комментарий, который заставляет синтаксический анализатор C# T4 игнорировать using... заявления и открытия namespace MyNamespace строки, которые в противном случае привели бы к синтаксической ошибке T4.
    • Это потому, что T4 требует using заявления должны быть выражены как <#@ import namespace="" #> директивы.
// */
  • Это завершающий разделитель для комментария открывающего блока за первым <#+.
  • В // скрывает */ из компилятора проекта C# (который не видит /*), а компилятор C# T4 увидит это, потому что // отменяется предыдущим /*.
    • Это работает, потому что в C# блочные комментарии будут "закомментировать" другие комментарии (если это имеет смысл!).
// #>
  • Наконец, T4 требует терминатора блока T4 перед EOF, поэтому мы используем те же ведущие -// трюк, чтобы скрыть это от C#, пока T4 все еще может его видеть.

Недостатки:

У этого подхода есть несколько недостатков:

  • Ведущий // будет отображаться в окончательный выходной файл.
    • Я не верю, что это можно смягчить.
    • Если вам известно решение, отредактируйте этот ответ или дайте мне знать в комментарии!
  • Включенный файл T4 не может объявлять импорт собственного пространства имен.
    • Хотя это не проблема для небольших сценариев T4, где не проблема обеспечить их добавление в сценарий точки входа T4.
    • Другой обходной путь - создать фактический *.ttinclude файл, в котором есть только необходимые <#@ import namespace="" #> директивы, а затем включает *.cs файл.
  • Включенный файл не может быть запущен как отдельный файл T4 из-за отсутствия <#@ template #> а также <#@ output #> директивы, которые, как я понимаю, должны быть помещены в начало файла.
    • Конечно, большинство включено *.ttinclude файлы в любом случае не могут быть выполнены сами по себе.

Включение файла C# в шаблон T4 с помощью:

<#@ include file="$(ProjectDir)ClassDefinition.cs" #>

Добавит текст к выводу шаблона T4. Он не компилирует класс.

В вашем шаблоне T4 задано значение debug = true, чтобы вы могли видеть, что генерирует T4, если загляните в каталог%TEMP%. При запуске шаблона T4 вы должны увидеть файл.cs, сгенерированный в каталоге TEMP. В этом файле у вас будет что-то похожее на:

 this.Write("public class ClassDefinition\r\n{\r\n    public string NameSpace { get; set; }\r\n    p" +
       "ublic string Name { get; set; }\r\n    public string Protection { get; set; }\r\n\r\n " +
       "  List<ClassProperty> Properties { get; set; }\r\n}");

Таким образом, все, что происходит с вашим классом C#, это то, что он будет записан в сгенерированный вывод T4.

Что вы, вероятно, хотите сделать, это включить файл ClassDefinition.cs в ваш проект, чтобы он был скомпилирован как часть вашего проекта. Затем вы можете ссылаться на сборку, которая включает в себя класс ClassDefinition. Таким образом, если ваш вывод проекта MyLibrary.dll, который содержит скомпилированный ClassDefinition.cs, то вы можете использовать:

<#@ assembly name="$(SolutionDir)$(OutDir)MyLibrary.dll" #>

Строка с файлом ClassDefinition.cs должна быть удалена.

Добавление этого после того, как ваши операторы импорта должны работать:

<#
ClassDefinition cd = new ClassDefinition();
#>
Другие вопросы по тегам