web-dev-qa-db-ja.com

T4-TT-TTファイルでカスタムクラスを使用する

TTのCSファイルで独自のクラス定義を使用したいと思います。

例:

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

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

私のTTは次のようになります:

<#@ 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ディレクティブまたはアセンブリ参照がありませんか?)

私はインターネットをチェックして、次のことを試みました。-インクルードを使用-アセンブリを使用-USINGを使用しかし、何も機能しません。

何か案は ?

11
Eagle

完全なソリューションは次のとおりです。

1)クラスを別のプロジェクトに分割します2)TT viaを介してこれらのクラスへの参照を含めます

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

3)このライブラリの参照をTTプロジェクトに含めることを忘れないでください

4)MyOwnLibraryProject.dllをTTソリューションのBIN\DEBUGフォルダーにコピーする必要があります

5)魔法が現れる!!!

DLL新しいバージョンをフォルダに入れることを忘れないでください:)または、ライブラリプロジェクトの出力をTT = 1。ガイドラインやアイデアを提供してくれた皆さんに感謝します。

16
Eagle

私があなたを正しく理解しているなら、あなたはテンプレート生成の一部としてクラスを再利用しようとしています。

そのクラスはttファイル自体に含まれている必要があり、ビルドアクションはnone、カスタムツール-noneに設定されています。私が持っているのは、上部に次のようなテンプレートマネージャークラスです。

<#@ 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"#>
5
reckface

以下を使用して、C#ファイルをT4テンプレートに含めます。

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

T4テンプレートの出力にテキストを追加します。クラスはコンパイルされません。

T4テンプレートにdebug = trueが設定されているので、%TEMP%ディレクトリを見るとT4が何を生成しているかを確認できます。 T4テンプレートを実行すると、TEMPディレクトリに生成された.csファイルが表示されます。このファイルには、次のようなものがあります。

 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ファイルを含む行を削除する必要があります。

1
Matt Ward

私自身も同じ問題を抱えていました-私の解決策は@Tehseenのようなものでしたが、実際の解決策を説明とともに提供します:)

任意のC#をT4ファイル(インポートされたC#を単に生のテキストとして含めるのではなくT4で使用する)に含めるコツは、T4がチョークする* .csファイルの部分を非表示にすることです-usingなどディレクティブ、およびタイプが<#+<#ではなく)ブロック内で宣言されていることを確認します。

これが私の解決策です:

私の「エントリポイント」MyScript.ttT4スクリプトは次のようになります。

<#@ 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 <#+区切り文字を認識しないようにします。これにより、プロジェクトの構文エラーが発生します。
  • ただし、<#+is T4パーサーによって解析され、ファイル内のC#がT4スクリプトで使用できるコードとして解釈されます。
  • 次の/*は、T4のC#パーサーがusing...ステートメントを無視し、namespace MyNamespace行を開く新しいコメントを開始します。そうしないとT4構文エラーが発生します。
    • これは、T4ではusingステートメントを<#@ import namespace="" #>ディレクティブとして表現する必要があるためです。
// */
  • これは、最初の<#+の後ろにある開始ブロックコメントの終了区切り文字です。
  • //はプロジェクトC#コンパイラ(*/を認識しません)から/*を非表示にしますが、T4のC#コンパイラは//がオーバーライドされるため、それを認識します。前の/*
    • これが機能するのは、C#ではブロックコメントが他のコメントを「コメントアウト」するためです(それが理にかなっている場合)。
// #>
  • 最後に、T4はEOFの前にT4ブロックターミネーターを必要とするため、同じリーディング//トリックを使用して、T4がまだ認識している間にC#から非表示にします。

短所:

このアプローチにはいくつかの欠点があります。

  • 先頭の//が最終出力ファイルにレンダリングされます。
    • これを軽減できるとは思いません。
    • 解決策をご存知の場合は、この回答を編集するか、コメント返信でお知らせください。
  • 含まれているT4ファイルは、独自の名前空間のインポートを宣言できません。
    • これは小さなT4スクリプトでは問題ではありませんが、すべてがエントリポイントT4スクリプトに追加されていることを確認することは問題ではありません。
    • 別の回避策は、必要な*.ttincludeディレクティブのみを含み、<#@ import namespace="" #>ファイルを含む実際の*.csファイルを作成することです。
  • インクルードされたファイルは、<#@ template #>および<#@ output #>ディレクティブがないため、独自のT4ファイルとして実行できません。ファイルの先頭に配置する必要があることを理解しています。
    • 確かに、含まれているほとんどの*.ttincludeファイルは、とにかく単独で実行することはできません。
0
Dai