14

特定のクラスを反映し、それらに基づいてコード生成を提供する T4 スクリプトを作成しています。問題は、現在のプロジェクトのクラスにアクセスできないと言って、スクリプトがエラーになることです。

スクリプト自体は、参照しようとしているクラスと同じアセンブリにあります。名前空間、ファイルを参照し、現在のアセンブリ (プロジェクト自体) への参照を追加しようとしましたが、すべて役に立ちませんでした。

私は何が欠けていますか?

4

4 に答える 4

10

これこそが、Niko と uosɐſ が探しているものだと思います。「MyAssembly.CodeGeneration」を T4 テンプレートを含むプロジェクトの名前に変更するだけです。

<#@ assembly name="$(TargetPath)MyAssembly.dll" #>
<#@ import namespace="MyAssembly.CodeGeneration" #>
于 2012-10-20T19:38:08.597 に答える
2

覚えておくべきことの 1 つは、作成している 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"を含むパブリック プロパティのみをリストする新しいコード ファイルを出力し、オブジェクト。私のプロジェクトのいくつかでは、このコードの適合バージョンを使用して POCO に基づいて JavaScript オブジェクトを生成し、シリアライゼーションの観点から、JS モデルがサーバー側オブジェクトと常に同期できるようにしています。gettersetter

ただし、その秘訣は最初の数行にあります。

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ますProjectsProject次に、currentTemplateFileが格納されている をロードし、FindClasses()メソッドで、検索条件に一致するそのプロジェクト内のクラスを解析します。

サンプルコードが出発点となることを願っていますが、さらに詳細が必要な場合は、次のいくつかの参考資料を参照してください。

于 2013-04-22T21:25:05.327 に答える