Question

Just so I don't get into an X-Y problem thing, what I'm wanting to do is to wrap WCF calls so that retry (and other rules) are implemented automatically, but I don't know all the interfaces ahead of time (it's a piece of middleware). So I basically taking the output of a generic DuplexChannelFactory<TChannel>.CreateChannel(), and then making that the proxy again. There's a few different questions on SO about wrapping calls, and retry and such, but none of them deal with having unknown number of interfaces that you need to have a completely generic solution to.

So I want to inject code every time the client calls things, but I want my object to implement the TChannel interface directly. So I thought of using Reflection.Emit to sub-class a "base" object that will hold the result of the DuplexChannelFactory<> call, and then connect up its own methods, including the retry functionality. Here's my initial "object creation" in my factory method:

    static TInterface generateImplementor<TInterface, Tcallback>(params object[] parameters) where TInterface : class
    {
        // Get the information about the interface.  This is necessary because this
        // is a generic method, where literally anything could be passed in
        Type interfaceType = typeof(TInterface);

        // Create the assembly and module to hold the object created
        // <snip>

        // Define a public class based on the passed-in interface name.
        TypeBuilder generatedType = myModule.DefineType(interfaceType.Name + "Implementor",
            TypeAttributes.Public, typeof(ForwarderBase<TInterface, Tcallback>),
            new Type[] { interfaceType });

        // Implement 'TInterface' interface.
        generatedType.AddInterfaceImplementation(interfaceType);

OK, but then where to go from there? This is something like what I came up with statically coded, but I need to do the last two calls there with Reflection.Emit.

class ForwarderBase<T, Tcallback>
{
    protected T proxyObj;
    public ForwarderBase(Tcallback callbackObj)
    {
        proxyObj = DuplexChannelFactory<T>.CreateChannel(callbackObj, "Endpoint");
    }

    private void noRetWrapper(Action call)
    {
        // Inject extra code here possibly
        try
        {
            call();
        }
        catch (Exception ex)
        {
            // all of this in a retry loop possibly, or whatever
            Console.WriteLine("Exception is: " + ex.ToString());
        }
    }

    private TRet retWrapper<TRet>(Func<TRet> call)
    {
        // Inject extra code here possibly
        try
        {
            return call();
        }
        catch (Exception ex)
        {
            // all of this in a retry loop possibly, or whatever
            Console.WriteLine("Exception is: " + ex.ToString());
        }
        return default(TRet);
    }

    // Dynamically emit these two, as depending on T, there will be an arbitrary number, with arbitrary arguments

    void firstMethod(int x)
    {
        // Lambda captures the arguments
        noRetWrapper(() => proxyObj.noReturnMethodCall(x));
    }

    int secondMethod(int firstParam, double secondParam, string thirdParam)
    {
        // Lambda captures the arguments
        return retWrapper(() => return proxyObj.returningMethodCall(firstParam, secondParam, thirdParam));
    }

}

So I have no problem Emitting those last two methods (and any number of methods really, it should be no problem), except for the capturing lambdas. That's necessary or else the two "wrappers" above explode into any possible number of types and return value combinations.

So how do I Emit the capturing lambdas I need? As this question says, there's no Func<...> or the like, hence my one Func, and one Action approach here.

Was it helpful?

Solution

This is what Castle DynamicProxy (and similar libraries) is for. With it you write an interceptor class that will get called every time a method on your proxy is called. That proxy is created automatically by calling a method.

The interceptor could look like this:

class IgnoreExceptionsInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        try
        {
            invocation.Proceed();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            invocation.ReturnValue = GetDefault(invocation.Method.ReturnType);
        }
    }

    private static object GetDefault(Type type)
    {
        if (type.IsValueType && type != typeof(void))
        {
            return Activator.CreateInstance(type);
        }
        return null;
    }
}

(GetDefault() has to be used, because there is no direct equivalent of default(T) that would take a Type.)

With an interface IFoo and its implementation Foo, you would use it like this:

var generator = new ProxyGenerator();
IFoo fooProxy = generator.CreateInterfaceProxyWithTargetInterface<IFoo>(
    new Foo(), new IgnoreExceptionsInterceptor());
fooProxy.Whatever();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top