0

クラスのメンバー関数を動的に呼び出して、指定された型を返すことができる汎用拡張メソッドを構築しようとしています。背景によっては、これが一般的な問題です。

Autorest を使用して、swagger API 用のクライアント ライブラリを生成しています。API 内の一部の GET ルートは、応答の HTTP ステータス コードに応じて異なるオブジェクトを返すため、メソッド呼び出しは返さobjectれ、開発者はオブジェクト自体をキャストする必要があります。このキャストを一般的な方法で行うための便利なラッパーを作成しようとしています。

以下は、まとめられる典型的な関数シグネチャの例です。

object IPet GetPets(string petName)

このメソッドは、HTTP ステータス コードに応じて、いくつかのオブジェクト タイプを返す場合があることに注意してください。たとえば、200 はオブジェクトを返しDogますが、404 はオブジェクトを返す場合がありCatます。

これは、次のように、Autorest によって生成されたクライアント ライブラリを通じて呼び出されます。

AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
object pet = animalClient.Pet.GetPets("dog");
if(pet is Dog) {
    Console.WriteLine("Dog!");
} else {
    Console.WriteLine("Not Dog");
}

この手動キャスト機能をもう少し直感的なものにすくい上げたいと思います。これが私が考えていることです:

AnimalApi animalClient = new AnimalApi(new Uri("http://myanimals.com"));
string petType = "Dog";
Dog pet = animalClient.Pet.CallMethod<IPet, Dog, string>( (api,type) => api.GetPets(type), petType);

このシナリオでは、'Dog' 型のオブジェクト以外を返すと、例外がスローされます。これが私の実装の試みです:

public static Tout CallMethod<Tin, Tout>(this Tin client, Expression<Action<Tin, Targ>> apiCall, params object[] args)
    where Tout : class {

    MethodCallExpression providedMethod = apiCall.Body as MethodCallExpression;
    if(providedMethod == null) {
        throw new ApplicationException("Invalid method call provded");
    }
    var method = providedMethod.Method;

    object responseData;
    try {
        // Call object-returning function
        responseData = method.Invoke(client, args);
    } catch(Exception error) {
        if(error.InnerException is HttpOperationException) {
            // Unknown error occurred
            var ex = error.InnerException as HttpOperationException;
            object content = JsonConvert.DeserializeObject(ex.Response.Content);
            throw new ServiceException(ex.Response.StatusCode + ": "
                + JsonConvert.SerializeObject(content));
        } else {
            throw error.InnerException;
        }
    }
    // Return formatted object if successful
    if(responseData is Tout) {
        return responseData as Tout;
        // Otherwise throw
    } else {
        // Deal with known error object responses
        if(responseData is ErrorResponse) {
            var error = responseData as ErrorResponse;
            throw new ServiceException(error);
        } else {
            // Unknown error occurred
            throw new ServiceException("Unknown response was received: "
                + JsonConvert.SerializeObject(responseData));
        }
    }
}

ここでの問題は、関数と引数を汎用拡張メソッドに渡すことです。さまざまな API 呼び出しで必要となる可能性のあるさまざまな数の引数を知らずに、どのようにExpression<Action<Tin, Targ>>一般的に定義できますか? Expression<Action<T1, T2, T3>>さまざまな長さの引数リストに対応するには、この関数などを複製する必要があるように思えます。

何が起こっているのかを簡単に確認できるように、人々が API と対話するための表現力豊かな方法が必要です。ただし、このメカニズムは、今後のさまざまな API の変更に対して堅牢である必要があります。私の現在の目標は、一般的なオブジェクトのキャストとエラー チェック操作をカプセル化する方法を提供することです。これを行うより良い方法はありますか?今のところ、サーバー側のswagger docは変更できないという前提で作業しています。

4

0 に答える 0