In the last article on call context initializers, the sample program included three bindings to try out. All the program did was trace the thread ID being used for a particular call when the service method was invoked or when the call context initializer was either creating or destroying the call context. The three bindings were:
Now, each of these bindings was created by extending the previous binding with additional binding elements. There weren't any setting changes or alterations to earlier binding elements in the channel stack. The service was using an instance per service call. If you ran the sample program with the first or third bindings, you'd get something like this:
Invoke: Client11Invoke: Client12Invoke: Client13Invoke: Client14Invoke: Client16Invoke: Client17Invoke: Client18Invoke: Client19Invoke: Client20Invoke: Client21BeforeInvoke: Server22Invoke: Client11 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client12 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client13 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client14 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client16 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client17 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client18 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client19 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client20 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client21 Server22AfterInvoke: Server22
Notice how all of the client calls are serialized and run on a single thread. If you ran the sample program with the second binding, you'd get a different looking result.
Invoke: Client11Invoke: Client12Invoke: Client13Invoke: Client15Invoke: Client16Invoke: Client17Invoke: Client18Invoke: Client19Invoke: Client20Invoke: Client21BeforeInvoke: Server22Invoke: Client11 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client12 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client13 Server22AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client15 Server22BeforeInvoke: Server23Invoke: Client16 Server23AfterInvoke: Server23AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client17 Server22BeforeInvoke: Server23Invoke: Client18 Server23BeforeInvoke: Server24Invoke: Client19 Server24AfterInvoke: Server22BeforeInvoke: Server22Invoke: Client20 Server22AfterInvoke: Server23BeforeInvoke: Server23Invoke: Client21 Server23AfterInvoke: Server22AfterInvoke: Server24AfterInvoke: Server23
It's only when you switch bindings that you actually get any concurrency. With the second binding, we finally see multiple threads and calls happening out of order. The key difference is that although there were multiple threads, all of the client calls for the sample program are being made on the same channel object. That means for the first and third binding that all of the calls are taking place on a single session channel. In the case of the first binding, the session comes from the TCP connection. In the case of the third binding, the session comes from using message security. In between these two cases, the one-way channel is discarding the TCP session because it consumes a duplex session channel and produces an input or output channel.
Next time: TryReceive and Exceptions