How to use SynchronizationContext with WCF

synchronizationcontext, wcf

Solution

There are a few things that stand out to me.

First, I can't recommend the use of a `SynchronizationContext` for this. You're trying to solve an application problem with a framework solution. It would work; I just find this questionable from an architectural perspective. The only alternatives aren't as clean, though: probably the most fitting would be to write an extension method for `Task` that returns a custom awaiter that preserves the `OperationContext`.

Secondly, the implementation of `OperationContextSynchronizationContext.Post` executes the delegate directly. There are a couple of problems with this: for one thing, the delegate should be executed asynchronously (I suspect there are a few places in the .NET framework or TPL that assume this). For another, this `SynchronizationContext` has a specific implementation; it seems to me that it would be better if the custom SyncCtx wrapped an existing one. Some SyncCtx have specific threading requirements, and right now `OperationContextSynchronizationContext` is acting as a replacement for those rather than a supplement.

Thirdly, the custom SyncCtx does not set itself as the current SyncCtx when it calls its delegate. So, it would not work if you have two `await`s in the same method.

Problem

I'm reading up on `SynchronizationContext` and trying to make sure I'm not messing anything up by trying to flow the `OperationContext` to all threads, even after an `await` call. I have this `SynchronizationContext` class: ``` public class OperationContextSynchronizationContext : SynchronizationContext { // Track the context to make sure that it flows through to the next thread. private readonly OperationContext _context; public OperationContextSynchronizationContext(OperationContext context) { _context = context; } public override void Post(SendOrPostCallback d, object state) { OperationContext.Current = _context; d(state); } } ``` Which is then called like this around every method call (using a `Ninject` `IInterceptor`): ``` var original = SynchronizationContext.Current; try { // Make sure that the OperationContext flows across to the other threads, // since we need it for ContextStack. (And also it's cool to have it.) SynchronizationContext.SetSynchronizationContext(new OperationContextSynchronizationContext(OperationContext.Current)); // Process the method being called. invocation.Proceed(); } finally { SynchronizationContext.SetSynchronizationContext(original); } ``` It seems to work (I'm able to use the OperationContext as needed), but is this the right way to do it? Am I missing anything important that might bite me later? EDITed with some of Stephen Cleary's comments: ``` public class OperationContextSynchronizationContext : SynchronizationContext, IDisposable { // Track the context to make sure that it flows through to the next thread. private readonly OperationContext _context; private readonly SynchronizationContext _previous; public OperationContextSynchronizationContext(OperationContext context) { _context = context; _previous = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(this); } public override void Post(SendOrPostCallback d, object state) { OperationContext.Current = _context; d(state); //(_previous ?? new SynchronizationContext()).Post(d, state); } private bool _disposed = false; public void Dispose() { if (!_disposed) { SynchronizationContext.SetSynchronizationContext(_previous); _disposed = true; } } } ``` FINAL: ``` public class OperationContextSynchronizationContext : SynchronizationContext, IDisposable { // Track the operation context to make sure that it flows through to the next call context. private readonly OperationContext _context; private readonly SynchronizationContext _previous; public OperationContextSynchronizationContext() { _context = OperationContext.Current; _previous = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(this); } public override void Post(SendOrPostCallback d, object state) { var context = _previous ?? new SynchronizationContext(); context.Post( s => { OperationContext.Current = _context; try { d(s); } catch (Exception ex) { // If we didn't have this, async void would be bad news bears. // Since async void is "fire and forget," they happen separate // from the main call stack. We're logging this separately so // that they don't affect the main call (and it just makes sense). // log here } }, state ); } private bool _disposed = false; public void Dispose() { if (!_disposed) { // Return to the previous context. SynchronizationContext.SetSynchronizationContext(_previous); _disposed = true; } } } ```

Original source