2

I am added a value to the InvocationContext dictionary, however it doesn't persist to the next call. i.e. each call in the chain that's intercepted is returning false for InvocationContext.ContainsKey("tracing-id").

public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
{
    Guid tracingId;

    if (!input.InvocationContext.ContainsKey(TRACING_ID))
    {
        tracingId = _tracingIdProvider.NewTracingId();
        input.InvocationContext.Add(TRACING_ID, tracingId);
    }
    else
    {
        tracingId = (Guid)input.InvocationContext[TRACING_ID];
    }

    var methodReturn = getNext()(input, getNext);

    return methodReturn;
}

I can add an entry to the InvocationContext, however, when getNext()(input, getNext) is invoked and the next call is intercepted the InvocationContext is empty.

Am I misunderstanding how InvocationContext is used? If so, what's the correct way to persist something like an ID from one method call to the next?

BanksySan
  • 27,362
  • 33
  • 117
  • 216
  • What kind of interceptor are you using? – cynic Jan 30 '15 at 13:16
  • An `InterfaceInterceptor` – BanksySan Jan 30 '15 at 14:08
  • I've briefly looked at Unity source code and the interface interception is the least readable due to all the code generation going on. Can you try with transparent proxy interceptor, or virtual method interceptor, if possible? If these work, the interface interception implementation may simply have a bug. In any case, you can use thread local storage or builtin CallContext to achieve what you want. – cynic Jan 31 '15 at 21:16
  • @cynic It's the same behaviour for all three interceptor types `VirtualMethodInterceptor`, `TransparentProxyInterceptor` and `InterfaceInterceptor`. Thread Local Storage might have some hope for this though. – BanksySan Jan 31 '15 at 22:04
  • @cynic Using TLS seems to be doing the trick. Can you think of anything that can go wrong here, that wouldn't have been a problem if I was able to use the `InvocationContext`? – BanksySan Feb 01 '15 at 00:00
  • You have to remember about cleanup yourself - if you have code that adds to the context, don't forget to have a branch there that removes the value after the invocation is done, preferably in a finally block. – cynic Feb 01 '15 at 12:10
  • Currently have no way to spot the start and end of a chain of calls, so no way to trigger clean up. – BanksySan Feb 01 '15 at 12:41

1 Answers1

2

My tests shows that the content of InvocationContext property is persisted only during one call. It means that your code will work only when there are many behaviors registered. They will be invoked one by one for the same method call and they can use InvocationContext to share data. However, as you observed, for the next call InvocationContext will be cleared.

I don't have much experience with Unity so I don't know what is the recommended workaround. You may consider to create your own invocation context and implement it as a singleton. Thanks to that all calls will have access to the same instance and TRACING_ID will be persisted across calls. It should work.

However, the question is when the invocation context should be cleared. If you don't do that, then all calls will have the same TRACING_ID. It doesn't have sense. For example, you may want to reset TRACING_ID when a particular method responsible for starting processing (a business scenario) is executed.

Michał Komorowski
  • 6,198
  • 1
  • 20
  • 24
  • Good spot, it does indeed seem that the context is persisted between calls to behaviours, but *not* down the call stack. Which explains one thing, but still leaves the remaining question. How do I persist an ID down the call stack? – BanksySan Jan 31 '15 at 21:06
  • @BanksySan - In my answer I also suggested a workaround i.e. _"...create your own invocation context and implement it as a singleton."_ It is a different approach than the one suggested by @cynic but a general idea is similar i.e. you need a global container to persist data across calls. You can also consider modification of Unity source code. Currently a new `InvocationContext` is created for every new call so you can try to add a new property (e.g. `GlobalInvocationContext`) that will be persisted across calls. However, if TLS suggested by @cynic works for you just use it. – Michał Komorowski Feb 01 '15 at 10:22
  • A global container would work, however, how would I know when to populate or clear it? What I mean is, how would I spot what it s new chain of calls? – BanksySan Feb 01 '15 at 12:40
  • I think that nobody can answer this question except you. You know your business scenario so you should also know what you actually want to track. For example a new chain of calls can start when a particular method is executed, when a new thread is started, when a new user has logged in... – Michał Komorowski Feb 03 '15 at 08:25
  • Good spot on noticing that it did pass between interceptors at the same position in the call stack! – BanksySan Feb 04 '15 at 21:15