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="" #>
директивы.
- Это потому, что T4 требует
// */
- Это завершающий разделитель для комментария открывающего блока за первым
<#+
. - В
//
скрывает*/
из компилятора проекта 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();
#>