Retryable interception behaviour

Oct 24, 2012 at 3:01 PM
Edited Oct 24, 2012 at 3:07 PM

I wanted to implement an RetryInterceptionBehaviour with Unity like this (contrieved example): 

        public IMethodReturn Invoke( IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext )
        {
            Console.WriteLine( "Behaviour: {0}", GetType().Name );

            bool retry;
            IMethodReturn ret = getNext()( input, getNext );
            while  ( ret.Exception != null )
            {
                // We could also log all parameters (input.Arguments)
                Console.WriteLine( "Retrying" );
                retry = mInteractionDialogService.Retry();
                if( retry )
                {
                    ret = getNext()( input, getNext );
                }
                else
                {
                    return ret;
                }
            }

            return ret;
        }
Unfortunally this doesn't work because the way InterceptionBehaviourPipeline is implemented in Unity. The original Invoke method in this class (which is the entry point for the first interception) is implemented like this:
        public IMethodReturn Invoke(IMethodInvocation input, InvokeInterceptionBehaviorDelegate target)
        {
            if (interceptionBehaviors.Count == 0)
            {
                return target(input, null);
            }

            int interceptorIndex = 0;

            IMethodReturn result = interceptionBehaviors[0].Invoke(input, delegate
                                      {
                                          ++interceptorIndex;
                                          if (interceptorIndex < interceptionBehaviors.Count)
                                          {
                                              return interceptionBehaviors[interceptorIndex].Invoke;
                                          }
                                          else
                                          {
                                              return target;
                                          }
                                      });
            return result;
        }
As you can see the next interceptor in the chain gets an anonymous delegate as getNext parameter with a captured index into a delegate array. This index never gets decremented. This means if you call getNext again in an interceptor this index will already have been "stuck" at the last entry in the array.
So repeated calls to getNext will only call the original method and ignore the interceptor chain below. A possible fix would be to decrement the index after a call to the next delegate in the chain returns.
For a quick hack i changed the implementation of Invoke into this:
        public IMethodReturn Invoke(IMethodInvocation input, InvokeInterceptionBehaviorDelegate target)
        {
            if (interceptionBehaviors.Count == 0)
            {
                return target(input, null);
            }

            int interceptorIndex = 0;

            IMethodReturn result = interceptionBehaviors[0].Invoke(input, delegate
                                      {
                                          ++interceptorIndex;
                                          if (interceptorIndex < interceptionBehaviors.Count)
                                          {
                                              return ( i2, getNext ) =>
                                              {
                                                  var methodReturn = interceptionBehaviors[ interceptorIndex ].Invoke( i2, getNext );
                                                  interceptorIndex--;
                                                  return methodReturn;
                                              };
                                          }
                                          else
                                          {
                                              return ( i2, getNext ) =>
                                              {
                                                  var methodReturn = target.Invoke( i2, null );
                                                  interceptorIndex--;
                                                  return methodReturn;
                                              };
                                          }
                                      });
            return result;
        }
This seems to work, but it doesn't look very nice.
Is there another way to achieve this without modifying Unity sources?
If not, is this probably a feature which could be considered for added in the future?
Regards, Gunter
Oct 24, 2012 at 6:28 PM

vNext is in planning stages so you can make a suggestion: http://entlib.uservoice.com.  

I think if you want to execute the entire chain of behaviors again then you will have to go with a similar approach with what you have posted.

It's an interesting question -- I'll think about it a bit more.  

--
Randy Levy
Enterprise Library support engineer
entlib.support@live.com