4

Is it possible to intercept the result of a WCF call and retry the operation?

For example, the return value of an operation may contain a status code that indicates that a session token that I passed in to the original call has expired. In this case, I can retrieve a new session token and retry the call with the new session token.

Is it possible to carry out this retry operation by using WCF interception of the return value, inspecting it, and then retrying the call totally transparently to the operation caller?

I already know how to inspect calls with IParameterInspector, but at this point there is no built-in way to retry the call.

I'm looking for a method that uses pure interception, so that it is totally transparent to the client.

Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130

3 Answers3

3

Assuming that you want this to happen on the client side then you can use an IClientMessageInspector - if you want to implement this on the server-side you can create an IDispatchMessageInspector.

By implementing an IClientMessageInspector you would inspect the result in the AfterReceiveReply event and if that is needing a retry then you can initiate the retry... when the call comes back you just "overwrite" the result that made you retry with the new... this way the operation caller doesn't even notice anything (except that the call sometimes takes a bit longer).

Although you must be careful in the implementation of the retry functionality (possible reentrancy issue needs to be handled appriopriately).

For some sample code (without the retry itself) see http://weblogs.asp.net/paolopia/archive/2007/08/23/writing-a-wcf-message-inspector.aspx

EDIT - as per comments:

How to implement the retry operation depends on several aspects.

Basically you need to associate a call to a request message and a request messages to a replay message.

One way you could do this is by implementing IClientMessageFormatter.SerializeRequest OR IParameterInspector - this will allow you to to record which method with which parameters have been called and what Message object the framework created for it.

By implementing IClientMessageInspector.BeforeSendRequest you can assign a unique correlationState which in turn allows you to relate the reply message in your implementation of IClientMessageInspector.AfterReceiveReply since the Framework will call your implementation with it as the second parameter.

From there you can use Reflection to retry the call (all needed information is available since you recorded the Type and the method and the parameters in your IClientMessageFormatter and/or IParameterInspector implementation) and overwrite the reply Message by the new reply.

Another (and perhaps much easier) option would be to implement a custom WCF client class and provide the caller with that... this allows you to stay outside the whole mess of different inspectors etc. and gives much directer control on any operation called.

EDIT 2 - as per comments:

When implementing a IClientMessageInspector you will have to also implement IEndpointBehavior which in turn has a method ApplyClientBehavior which is called by the WCF runtime to add your IClientMessageInspector. In that method you instantiate your implementation which takes one argument in the constructor which in turn is the endpoint.Contract.ContractType or clientRuntime.ContractClientType. you store this type in your inspector instance... for a hint of how to code this see for example http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/19500d14-78b7-4356-b817-fcc9abc2afcf/

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • So how would I actually replay the call in `AfterReceiveReply`? – Tim Lloyd Nov 05 '11 at 21:52
  • I can see how your answer allows me to inspect the returned message, but then I already know how to do that using IParameterInspector. How do I retry the call? – Tim Lloyd Nov 06 '11 at 07:20
  • @chibacity see my EDIT above with a description on the "how"... although I would recommend providing the WCF client with your own (extended) WCF client class avoiding the hassle... – Yahia Nov 06 '11 at 11:54
  • I know I can record the operation which is being called, but how exactly do I record the **type** that is being called so that I can then use reflection to replay the call? – Tim Lloyd Nov 11 '11 at 13:14
1

You could use an AOP framework for this. Unity has enough functionality for this, or you could consider using a "proper" AOP framework, such as LinFu or PostSharp.

Erik A. Brandstadmoen
  • 10,430
  • 2
  • 37
  • 55
  • Thanks for the answer, but WCF has built-in interception, I'd like to use it. Of course if this is a complete pain, I know how to intercept with Ninject :) – Tim Lloyd Nov 05 '11 at 20:15
  • I found this extremely hard to do with WCF built in facilities, but very easy with Castle DynamicProxy. With WCF message inspectors etc. you need a lot of voodoo incantations and boiler plate code (with no good documentation to be found); with Castle Dynamic Proxy you just write a simple interceptor plus two lines of code for proxy generation. So if you ever need to do something similar (I needed to), I strongly suggest going down the AOP / dynamic proxy framework route. – chris Jul 11 '13 at 16:52
0

I would use unity interface/virtualmethod interception.

_unityContainer.AddNewExtension<Interception>(); 

_unityContainer.RegisterType<ITestCaching, TestCaching>(
    new Interceptor<InterfaceInterceptor>(),
    new InterceptionBehavior<PolicyInjectionBehavior>());

_unityContainer.RegisterType<XRepository>(
    new Interceptor<VirtualMethodInterceptor>(),
    new InterceptionBehavior<PolicyInjectionBehavior>());

I use policy injection behavior so that I can use Attributes to add the additional behavior. custom attributes involved in interception will need to inherit HandlerAttribute and implement:

public override ICallHandler CreateHandler(IUnityContainer container)
{
    return new RetryCallHandler(container, ConfigurationName);
}

The behavior will be implemented in the class that implements ICallHandler

public class RetryCallHandler : ICallHandler, IDisposable
{

    public IMethodReturn Invoke(IMethodInvocation args, GetNextInterceptionBehaviorDelegate getNext) 
    { 
        Exception exception = null;
        int retryCounter = MAX_RETRIES;

        do
        {
            exception = null;
            try
            {
                var intercepted = getNext();
                IMethodReturn methodReturn = intercepted(args, getNext);

                return methodReturn;
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }while(exception!=null && retryCounter-- > 0);


        return args.CreateExceptionMethodReturn(exception);
    }
}

I haven't compiled or tested the above code.

The only problems that I foresee are GetNextInterceptionBehaviorDelegate in case you have multiple interceptions. You can manage the order of your call handlers so just make sure that this is the last in the chain.

Leblanc Meneses
  • 3,001
  • 1
  • 23
  • 26