다음을 통해 공유


Back from blogcation

Well, it's been a while, but I'm back from my extended blogcation.  Since my job has shifted again, I'm going to refocus this blog back to development side of things going forward.  Today I have made a cool discovery about OperationContextScopes that might save another developer some time, so I'm going to get started with something I hope will be useful.

Imagine you are building a client application which uses WCF, and you'd like to add some headers before you invoke the web method on a proxy.  The code might look something like:

OperationContextScope scope = new OperationContextScope(myProxy.InnerChannel);

MessageHeader<string> myHeader = newMessageHeader<string>("My header value);

MessageHeader headerWrapper = myHeader.GetUntypedHeader("MyHeaderName", "");

OperationContext.Current.OutgoingMessageHeaders.Add(headerWrapper);

myProxy.MyWebMethod()

Of course you'd want to package this up in some way so that this particular code can be reused for all your web method calls, but that is a different blog post.  Anyway, if you write your code this way, you might be surprised to learn that you are leaking OperationContextScope objects.  You can tell this is happening because looking in Task Manager you can see the memory consumption of your application grow, and grow as more web method calls are executed.  You can find out what is leaking by using this excellent tool called CLRProfiler (https://www.microsoft.com/en-us/download/details.aspx?id=16273).  Run your application for long enough, and if the application repeatedly calls web methods to get data to refresh its data model over time you'll see when you dump the heap using CLRProfiler that although the variable scope in the above example goes out of scope, it's storage is never reclaimed by the GC as you might expect it would.  As it turns out, WCF maintains a stack of OperationContextScope instances.  Making a new OperationContextScope instance pushes onto that stack, and disposing of the topmost OperationContextScope instance pops the stack.  If you never dispose of the OperationContextScope instance, the stack just keeps growing.  Because the stack is accessible, the GC isn't allowed to collect anything on the stack.  Thus the leak.  To fix the above code is super simple once you've figured out the problem:

 

using (OperationContextScope scope = new OperationContextScope(myProxy.InnerChannel))

{

MessageHeader<string> myHeader = newMessageHeader<string>("My header value);

MessageHeader headerWrapper = myHeader.GetUntypedHeader("MyHeaderName", "");

OperationContext.Current.OutgoingMessageHeaders.Add(headerWrapper);

myProxy.MyWebMethod()

}

There are a lot of other things you need to be careful about, like carefully closing the proxy, but that is also for another blog post.  Meantime, happy coding!