4

私はT4で手を出し始めて、最初はかなりうまくいきましたが、実際にはかなり明白で解決できないかもしれない問題に遭遇しましたが、私が知ることや見ることの経験が不足している方法があるかもしれません。

次のクラスがあるとします。

public class T4Test : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
    }

    [Input("InX")]
    public InArgument<string> InX { get; set; }

    [Output("OutX")]
    public OutArgument<string> OutX { get; set; }
}

私はこれを出力として欲しい:

public class ActivityWrapper
{
    private readonly T4Test _activity;
    private readonly ActivityContext _context;

    public ActivityWrapper(T4Test activity, ActivityContext context)
    {
        this._activity = activity;
        this._context = context;
    }

    public string InX
    {
        get { return this._activity.InX.Get(this._context); }
    }

    public string OutX
    {
        get { return this._activity.OutX.Get(this._context); }
        set { this._activity.OutX.Set(this._context, value); }
    }
}

私は必要なReflectionのものを理解し、T4コードがどのように見えるかを知っていますが、1つの問題があります。それは、T4Testクラスと同じプロジェクトで必要です。ただし、アセンブリをロードして反映するには、コンパイルする必要があります。ただし、同じアセンブリのコードを変更する場合は、もちろんそれは少し難しいです。(そして、NCrunchは物事を単純化しないと思います。)

これが、これを解決できる可能性があることを私が望んでいることです。

  • プロジェクト、生成されたクラスなしでコンパイルされます。これは、クラスがIoCコンテナによって自動登録/解決されるインターフェイスを実装するためです。ActivityContextまた、モックできないため、とにかくテストできません。
  • そのため、常にそこにある必要はなく、修正する必要もありません。DLLを実際に配信する前に、「今すぐ生成」と言うことができれば十分です。
  • 同じ理由で、T4テンプレートが実際にプロジェクトにあるかどうかも気にしません-生成されたファイルがプロジェクトに含まれる限り(ただし、テンプレート用に別のプロジェクトを必要とせず、ファイルをコピーするためのPostBuildイベントを作成する必要はありません.cs) 。
  • 正確には、T4である必要はありません。他に実行可能な方法があれば、それも喜んで使用します。

これを達成する方法はありますか?(そしてそれは十分に明確でしたか?)

4

2 に答える 2

6

T4の変換は、プロジェクトが正常にビルドされ、アセンブリが古くない場合に適切な出力を生成する場合にのみ機能するため、生成されたアセンブリを反映する代わりの方法を提案したいと思います。

ホスト固有のT4テンプレートを使用する場合は、EnvDTEインターフェイスを介してVisualStudio自動化モデルにアクセスできます。これを使用すると、最初にビルドしなくても、現在ロードされているVisualStudioソリューションのCodeModelをウォークスルーできます。

このSOの質問に対する私の答えを見てください:DesignTimeReflection。tangibleのテンプレートギャラリーの無料テンプレートを使用すると、設計時に既存のクラスを簡単に「反映」し、目的の属性で装飾されたプロパティを検出できます。

<#
var project = VisualStudioHelper.CurrentProject;

// get all class items from the code model
var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false);

// iterate all classes
foreach(EnvDTE.CodeClass codeClass in allClasses)
{
    // iterate all properties
    var allProperties = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.Members, EnvDTE.vsCMElement.vsCMElementProperty, true);
    foreach(EnvDTE.CodeProperty property in allProperties)
    {
        // check if it is decorated with an "Input"-Attribute
        if (property.Attributes.OfType<EnvDTE.CodeAttribute>().Any(a => a.FullName == "Input"))
        {
            ...
        }
    }
}
#>
于 2013-03-26T19:53:53.920 に答える
3

T4Test.tt

<#@ include file="Activities.tt" #>
<#
var t4test = new Activity("T4Test")
{
    Input("InX"),
    Output("OutX"),
};
GenerateCode(t4test);
#>

Activities.tt

<#@ template language="C#" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#+
class Activity : IEnumerable<Property>
{
    private string name, wrapper;
    private List<Property> properties;
    public Activity(string name, string wrapper = null)
    {
        this.name = name;
        this.wrapper = wrapper ?? name + "Wrapper";
        this.properties = new List<Property>();
    }
    public void Add(Property property)
    {
        this.properties.Add(property);
    }
    public IEnumerator<Property> GetEnumerator()
    {
        return this.properties.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public void GenerateCode()
    {
        // ...
    }
}
class Property
{
    private bool output;
    private string name, type;
    public Property(bool output, string name, string type)
    {
        this.output = output;
        this.name = name;
        this.type = type;
    }
}
Property Input(string name, string type = "string")
{
    return new Property(false, name, type);
}
Property Output(string name, string type = "string")
{
    return new Property(true, name, type);
}
void GenerateCode(params Activity[] activities)
{
    WriteLine("namespace Foo");
    WriteLine("{");
    PushIndent("   ");
    foreach (var activity in activities)
    {
        WriteLine("class " + activity.name);
        WriteLine("{");
        PushIndent("   ");
        // ...
        PopIndent();
        WriteLine("}");
    }
    PopIndent();
    WriteLine("}");
}
#>
于 2013-03-26T01:30:41.050 に答える