5
\$\begingroup\$

GitHub and NuGet

I would like to use DynamicObject derived types to execute REST API/NodeJS module calls. It would also help to capture API shape in a strictly typed manner, so here comes an interface proxy which hides low level machinery allowing to do tricks like this:

[TestMethod]
public void Call()
{
    dynamic c = new ExpandoObject();
    c.Add = (Func<int, int, int>)((a, b) => a + b);

    ICalculator proxy = Proxy.Create<ICalculator>(c);
    Assert.AreEqual(3, proxy.Add(1, 2));
}

public interface ICalculator
{
   int Add(int a, int b);
}

Library code is:

using Castle.DynamicProxy;
using Dynamitey;
using System;

namespace Dynoproxy
{
    public static class Proxy
    {
        public static T Create<T>(this object source) where T : class
        {
            var proxyGenerator = new ProxyGenerator();
            return proxyGenerator.CreateInterfaceProxyWithoutTarget<T>(
                ProxyGenerationOptions.Default,
                new Interceptor(source));
        }

        class Interceptor : IInterceptor
        {
            public Interceptor(object target) => Target = target;
            object Target { get; }
            public void Intercept(IInvocation invocation) =>
                invocation.ReturnValue = Dynamic.InvokeMember(
                    Target, invocation.Method.Name, invocation.Arguments);
        }
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

It would be very useful to see the associated MethodInfo instance while implementing source in the situation like this:

interface IMyWebApi
{
    [Description("GET orders/{0}")]
    Order GetOrder(int id); 
}

IWebApi api = Proxy.Create<IMyWebApi>(new RestApi("http://example.com/api"));
Order order = api.GetOrder(33);

So I made the proxy injecting it when DescriptionAttribute is provided:

[TestMethod]
public void Call()
{
    dynamic c = new ExpandoObject();
    c.Add = (Func<int, int, int>)((a, b) => a + b);
    c.Divide = (Func<MethodInfo, int, int, int>)((mi, a, b) => a / b);

    ICalculator proxy = Proxy.Create<ICalculator>(c);
    Assert.AreEqual(3, proxy.Add(1, 2));
    Assert.AreEqual(2, proxy.Divide(4, 2));
}

public interface ICalculator
{
    int Add(int a, int b);

    [Description]
    int Divide(int a, int b);
}

It should provide access to all interface method attributes and return types.

Updated Interceptor looks like this now:

class Interceptor : IInterceptor
{
    public Interceptor(object target) => Target = target;
    object Target { get; }
    public void Intercept(IInvocation invocation) =>
        invocation.ReturnValue = Dynamic.InvokeMember(
            Target,
            invocation.Method.Name,
            invocation.Method.IsDefined(typeof(DescriptionAttribute)) 
                ? invocation.Arguments.Prepend(invocation.Method).ToArray()
                : invocation.Arguments);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.