Search code examples
c#genericstaskcastle-dynamicproxydynamic-proxy

Async Interception using Castle dynamic proxy


I'm trying to build a dynamic http client using dynamic proxy to intercept the calls and create an http request with it.

The issue i had was with Async methods:

private Task<object> PostAsync(HttpClient client, string url, HttpRequestParameters parameters, Type returnType)
    {
        return Task.Run(async () =>
        {
            var requestContent = new StringContent(Serializer.Serialize(parameters.BodyParameters));
            var httpResponse = await client.PostAsync(url, requestContent);
            var responseContent = await httpResponse.Content.ReadAsStringAsync();
            return Serializer.Deserialize(responseContent, returnType);
        });
    }

My task returns dynamic/object and not the T of the Interception return type.

I thought that i will be able to use it like so

 var task = PostAsync(client, url, parameters, returnType);
 invocation.ReturnValue = task;

Since the task that will be returned is the original task and it is still pending i thought it would work but all i'm getting is an exception that Task cant be converted to task of my type (Which is string in that case).

Thanks for the helpers

Edit: I did see Intercept async method, that's what i tried to do but i was unable to call the Task even using reflection, i still got the same exception.


Solution

  • I solved it eventually with a few modifications:

    1. Creating the interceptor with a base object, i used Moq objects to lazy create them and store them in a ConcurrentDictionary for caching.

      var mock = new Mock<T>();
      var pg = new ProxyGenerator();
      return pg.CreateInterfaceProxyWithTarget<T>(GetTarget(clientType), _gatewayInterceptor);
      
    2. I passed the invocation's return value (in that case Task of T) to a method and got the T.

    3. I wrapped the http call with a new Task of T, await the http call and return desirialized T result from the task.
    4. Assign the new Task of T back to the return value.

      invocation.ReturnValue = GetAsync((dynamic)invocation.ReturnValue, serializer, headers, req);
      
      internal static Task<T> GetAsync<T>(Task<T> originalTask, ISerializer serializer, Headers headers, InvokeHttpRequest req)
      {
          return Task.Run(async () =>
          {
              using (HttpClient client = new HttpClient())
              {
                  var httpResponse = await PerformGetAsync(headers, req, client);
                  var jsonResponse = await httpResponse.Content.ReadAsStringAsync();
                  return ProcessResult<T>(serializer, jsonResponse);
              }
          });
      }
      

    I know its not the best way to go but it worked for me. The solution is here if anyone needs it https://github.com/ErezLevip/SimpleProxyClient