3

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.

Community
  • 1
  • 1
Kevin Anderson
  • 6,850
  • 4
  • 32
  • 54
  • i'm not really sure if i understood your problem ... can't you just build an Expression Tree with Expression and then call CompileToMethod(...)? – DarkSquirrel42 Apr 07 '13 at 19:36
  • I'm not familiar with Expression Trees. Have any links? Also remember that the lambdas will be slightly different (invoke different methods, capture different variables) on every emitted method. If that's OK, then great, but I don't know anything about expression trees. – Kevin Anderson Apr 07 '13 at 21:02
  • http://msdn.microsoft.com/en-us/library/bb397951.aspx – DarkSquirrel42 Apr 07 '13 at 22:10
  • Can you please add an answer DarkSquirrell42 that only gives how to create an Expression Tree that captures all the arguments? Basically, if I needed to create the `secondmethod()` from my example, how would I do that? The doc on doing closures with expression trees is not easy to find. – Kevin Anderson Apr 07 '13 at 23:44
  • The problem is: i'm not familiar with emit ... my idea was to introduce a private member to the generated type (member type can be generated by Expression.GetDelegateType()) to have a Field that holds your closure ... the closure itself needs access to the instance therefore it can't be created before the instance is created ... the idea is to refer to that closure as some field that holds a delegate, and therefore can be initialized after the instance has been created – DarkSquirrel42 Apr 08 '13 at 13:06

1 Answers1

2

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();
Community
  • 1
  • 1
svick
  • 236,525
  • 50
  • 385
  • 514
  • It's maybe worth looking at, though the documentation is... sparse. The interface proxy with target interface is what I need (because I need to re-generate the `IChannel` but the fact the switch doesn't "stick" is not what I need. See here: http://kozmic.net/2009/04/27/castle-dynamic-proxy-tutorial-part-x-interface-proxies-with-target/ Unless that's out of date. Is there better doc somewhere? Their homepage links to their wiki, which doesn't qualify as better. – Kevin Anderson Apr 07 '13 at 21:18
  • @Kevin I don't understand, what do you mean that the switch doesn't stick? – svick Apr 07 '13 at 21:54
  • It's bolded in one of the last paragraphs of my link that when you switch a target, it's only switched for that invocation, not permanently, and that there's actually an issue where if you have multiple invocations going at once you have to be really really careful about how that works. – Kevin Anderson Apr 07 '13 at 23:12
  • @Kevin But why would you want to change the target? I think you don't need that in your case and that the code from my answer should be enough. – svick Apr 07 '13 at 23:27
  • I didn't give a "100% complete" example. I also need to retry, so that the `proxyObj` from my example is re-assigned if an exception is thrown, and then the lambda is tried again. That requires the "interface proxy with target interface" version of DynamicProxy, and thus then the link I provided. – Kevin Anderson Apr 07 '13 at 23:33
  • @Kevin I think you should consider asking a new question about that, because I still don't understand what exactly do you mean and why would that require reassigning. – svick Apr 08 '13 at 00:34
  • All I want is how to emit firstMethod and secondMethod (including the Lambdas therein) from my example. That's it. The rest is to make sure there isn't already another way of doing this with WCF. – Kevin Anderson Apr 08 '13 at 21:19
  • I know, but I probably can't use a 3rd-party library (constraints I can't control), so need the implement the raw emit stuff myself. I did see a different tutorial for Castle DynamicProxy that showed an idea on how to do what I needed, so the framework WOULD work: http://brandonzeider.me/2011/microsoft-net/building-a-reusable-service-client/ Unfortunately I probably won't get to use it. Client constraints! I'm going to mark you as "answer" though. – Kevin Anderson Apr 08 '13 at 23:03