4

Visual Studio 2010 (Express ではない) と MSBuild を使用して、ビルド プロセスの一部としてコードとジェネレーター コードを検証できる方法を探しています。

バックグラウンド検証:

WCF Web Api を使用して RESTful Web サービスを作成しています。Web サービスを表すサービス クラス内で、エンドポイントを定義し、追加のパラメーターを単純なテストとして宣言する必要があります。エンドポイント宣言内のパラメーター名が C# メソッドのパラメーターと異なる場合、エラーが発生します。残念ながら、コンパイル時ではなく、Web サービスにアクセスする実行時に発生します。そのため、このような欠陥のコンパイル手順の一部として Web サービス クラスを分析し、何かが正しくない場合にエラーを返すとよいと考えました。

例:

[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public string MyMethod(string param1, string parameter2) {
    // Accessing the web service now will result in an error,
    // as there's no fitting method-parameter named "param2".
}

また、GET メソッドは「Get」という単語で始まる必要があるなど、いくつかの命名規則を適用したいと考えています。これにより、複数の同僚と作業する場合に、サービスをより保守しやすくすることができると思います。

バックグラウンド生成:

このサービスにアクセスするためのクライアントを作成する必要があるため、他のいくつかのプロジェクトでこの REST Web サービスを使用します。しかし、サービスが変更されるたびに常に調整して、これらのそれぞれのクライアントを作成したくありません。Web サービス コード ファイルに基づいて、クライアントを自動的に生成したいと考えています。

以前のアプローチ:

これまでのところ、DTE インターフェイスを使用して T4 テンプレートを使用して、コード ファイルを解析して検証するか、クライアントを生成しようとしました。手動で保存する場合、これは Visual Studio で正常に機能しましたが、これをビルド プロセスに統合すると、MSBuild を使用して Visual Studio ホストを使用できないため、うまく機能しないことが判明しました。

どんな提案でも大歓迎です。:)

4

4 に答える 4

2

DTEまたはその他の手段を使用してC#コードを解析する代わりに、リフレクション(Reflection-Onlyコンテキストを使用)を使用して、コンパイル後にアセンブリを調べることができます。リフレクションを使用することは、より堅牢なソリューションであり、おそらくより高速です(特に、Mono.Cecilを使用してリフレクションを行う場合)。

MSBuildの統合については、カスタムMSBuildタスクを作成することをお勧めします。MSBuildによって実行されるコマンドラインユーティリティを作成するよりも、かなり簡単で、堅牢でエレガントです。

于 2012-01-26T01:51:53.500 に答える
2

以下は、WebGet UriTemplate チェックを実行するために、アセンブリまたはアセンブリのグループに対して実行できる (単に dll を引数として渡す)短い非常に醜いプログラムです。何も渡さない場合は、それ自体で実行されます (独自の単体テストであるため、適切に失敗します)。

プログラムは、パラメーターが欠落しているメソッドの名前と欠落しているパラメーターの名前を stdout に出力し、いずれかが見つかった場合は、ゼロ以外の戻りコード (プログラムが失敗した場合の標準) を返します。ビルド後のイベントとして。あなたの目が出血した場合、私は責任を負いません。

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;

namespace ConsoleApplication1
{
    class Program
    {
        static int Main(string[] args)
        {
            var failList = new ConcurrentDictionary<MethodInfo, ISet<String>>();
            var assembliesToRunOn = (args.Length == 0 ? new[] {Assembly.GetExecutingAssembly()} : args.Select(Assembly.LoadFrom)).ToList();
            assembliesToRunOn.AsParallel().ForAll(
                a => Array.ForEach(a.GetTypes(), t => Array.ForEach(t.GetMethods(BindingFlags.Public | BindingFlags.Instance),
                    mi =>
                        {
                            var miParams = mi.GetParameters();
                            var attribs = mi.GetCustomAttributes(typeof (WebGetAttribute), true);
                            if (attribs.Length <= 0) return;
                            var wga = (WebGetAttribute)attribs[0];
                            wga.UriTemplate
                                .Split('/')
                                .ToList()
                                .ForEach(tp =>
                                             {
                                                 if (tp.StartsWith("{") && tp.EndsWith("}"))
                                                 {
                                                     var tpName = tp.Substring(1, tp.Length - 2);
                                                     if (!miParams.Any(pi => pi.Name == tpName))
                                                     {
                                                         failList.AddOrUpdate(mi, new HashSet<string> {tpName}, (miv, l) =>
                                                                                                                    {
                                                                                                                        l.Add(tpName);
                                                                                                                        return l;
                                                                                                                    });
                                                     }
                                                 }
                                             });
                        })));
            if (failList.Count == 0) return 0;
            failList.ToList().ForEach(kvp => Console.Out.WriteLine("Method " + kvp.Key + " in type " + kvp.Key.DeclaringType + " is missing the following expected parameters: " + String.Join(", ", kvp.Value.ToArray())));
            return failList.Count;
        }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillPass(String param1, String param2) { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail() { }

        [WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
        public void WillFail2(String param1) { }
    }
}
于 2012-01-26T02:59:24.163 に答える
2

施行面では、カスタム FxCop ルールがおそらく非常に適しています。

クライアント コードの生成には、かなりの数の可能性があります。T4 アプローチが気に入った場合は、MSBuild で動作させる方法がある可能性があります (ただし、現在動作していないものについてもう少し詳しく説明する必要があります)。とにかく別の方法が必要な場合は、リフレクション ベースのビルド後のツールを使用するのも 1 つの方法です...

于 2012-01-26T01:41:05.803 に答える
2

これはロングショットかもしれませんが、それでも「任意の提案」としての資格があります:)

コードをコンパイルしてから、リフレクションを使用して解析された UriTemplate テキストをメソッド パラメーター名と比較し、エラーをキャッチして、MSBuild が実行する方法でそれらを出力するツールを作成する必要があるビルド後のコマンドを実行できます。ピックアップします。MSBuild がエラーをビジュアル スタジオのエラー リストに入れるように出力する方法については、このリンクを参照してくださいエラーが見つかった場合、ビルド後のツールはコンパイル済みのアセンブリを削除し、失敗したビルドを「シミュレート」します。

参考までに、MSBuild ブログにもつながるSO リンクを次に示します。

HTH

于 2012-01-26T01:13:23.620 に答える