动态成员调用的通用扩展方法

时间:2016-12-14 22:01:17

标签: c# generics lambda extension-methods autorest

我正在尝试构建一个通用的扩展方法,它可以动态调用类的成员函数并返回指定的类型。对于某些背景,这是一般问题:

我正在使用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);

在这种情况下,除了&#39; Dog&#39;会导致异常被抛出。以下是我的实施尝试:

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无法改变。

0 个答案:

没有答案