April 2006 - Posts
Seeing as our project is all distributed and stuff, with various services possibly running on different servers, we wanted to have the whole service address business be flexible, and not rely on hardcoded values in app.config files.
We have a Configuration service, which is the only hardcoded address, and it can return the address for any given service.
The item we're using here is my own ServiceAddress object, which contains an EndpointAddress object and a Binding object for a given service. This way we can easily have a service switch from TCP to HTTP binding if necessary without redeploying. All is well.
The problem is that this configuration is stored on the Config server as an XML file with the ServiceAddress object serialized to XML. This means that the Binding object is also serialized to XML, and there lies the problem - the TimeSpan structure in .NET 2.0 cannot be serialized to XML. It loses all data and becomes an empty node, recreated as a 0-length TimeSpan when deserialized.
Since the Binding class has several TimeSpan properties for the timeout values, my service agents timeout immediately when attempting to contact the service.
Apparently the TimeSpan problem is a well-known one (here
), but the common workaround (of adding a new property with the TimeSpan's Ticks value) isn't really feasible a WinFX framework class.
I can extract and store the values manually during the serialization phase, but that seems ridiculously inelegant.
Another idea is to store the configuration files in the same format as they are defined in the app.config, and use the ClientSection class from System.ServiceModel.Configuration namespace to save and load the data. This is probably more complicated since I don't know if I can generate a <client> section from a Binding object. This means I can only read the configuration, not write to it from within my program.
Anyone have any other ideas?
Just a quick tip about the some new problems we're having with VSS:
To ease integration and control, all developers on the team have their local development drive mapped to drive P:\, to make sure all file references are consistent between developers. Since it's just a subst-mapping and actually points to the original location, some of us opened the solution from the original location expecting it to work.
It does not
The two locations might be identical on the local disk, but as far as VSS is concerned they are two different locations with different data. If you checked out a file in P:\ and try to check it in from C:\MyProject\, VSS will complain that you are checking it in from a different location. This might lead to any number of annoying errors and even loss of data if you're not careful. So be careful, and always open your solution from the same location.
At last, a solution presented itself. While I must admit that at first I was very skeptical of the extensibilty model for WCF which seemed far too involved and complicated, but after implementing a simple extension I must say it's simple and quite intuitive.
My goal was to have several custom headers added to each call made through a proxy, rather than manually adding them to each call's OperationContext. With a little help from Ralph Squillace's blog
, I was able to get an extension up and running within minutes, and it Just Works.
The first item to build is the actual extension logic. In this case, I needed an IProxyMessageInspector object that inspects each outgoing message and adds the custom headers:public class ContextPusher : IProxyMessageInspector
public void AfterReceiveReply(ref Message reply, object correlationState)
// Do nothing.
public object BeforeSendRequest(ref Message request, IClientChannel channel)
MessageHeader myHeader = new MessageHeader("HeaderValue").GetUntypedHeader("MyHeader", "ns");
Now we want to attach that inspector to proxy. We do that wth Behavior objects. This can be done on an operation-by-operation basis or for all operations on a given channel. We can make the same class implement both behaviors, for flexibility:public class CallContextAttribute : Attribute, IChannelBehavior, IOperationBehavior
#region IOperationBehavior Members
public void ApplyBehavior(OperationDescription description, ProxyOperation proxy, BindingParameterCollection parameters)
// Add my MessageInspector to each message sent by the proxy.
public void ApplyBehavior(OperationDescription description, DispatchOperation dispatch, BindingParameterCollection parameters)
// No server-side behaviors for now.
#region IChannelBehavior Members
public void ApplyBehavior(ChannelDescription description, ProxyBehavior behavior, BindingParameterCollection parameters)
// Add the MessageInspector to every message sent through the channel.
And then simply put it on a Service or Operation.
I think in this case, putting the attribute on both will result in an error when trying to add duplicate headers. But this allows me flexibility in adding the headers only to certain calls.[ServiceContract]
public interface ILocalizationServices
string DoSomething(string param);
I think this is the first time I got really excited by the WCF framework and the ease of using and extending it. This is FUN.
One thing I still haven't managed to do is create my proxy in such a way that a custom header is added to every call that is made through that proxy.
Currently, this is done by instantiating a new OperationContext around each call through the proxy. This can be optimized in several ways:
1) Create a shared OperationContext object and always instatiate the OperationContextScope with it:using (OperationContextScope scope = new
I don't know if it's a good idea, though. Should OperationContext objects be persisted between calls, or would it have unexpected side effects?
2) Create a wrapper around the OperationContextScope class that fills the OperationContext with the proper headers:public class MyOperationContextScope : IDisposable
public MyOperationContextScope ()
contextScope = new OperationContextScope();
MessageHeader standardHeader = new
public void Dispose()
if (contextScope != null)
This is a bit cleaner, but still requires me to wrap every proxy call with a using() statement.
Is there a way to interecept all calls and have my headers added automatically?
again, writes about adding custom headers
to a call made by a WCF proxy.
This is all very nice and well, but his code only works with the Feb. CTP of WCF, while I'm still using January here.
The differences seemed subtle at first - his instantiation of the OperationContextScope passed the proxy to the constructor, but there is no such constructor in the January CTP.
Simply instantiating the OperationContextScope with no parameters and adding headers seemed to work, but those headers never made it to the server-side.
After much fussing and digging, it turns out that the headers were
transferred, but the way to fetch them was obscure. I don't know if this was a design change from Jan to Feb or maybe it was just a bug that was fixed, but instead of accessing the headers like this:
string myHeader = OperationContext.Current.IncomingMessageHeaders.GetHeader("myHeader", "myNS");
we have to do it like this:string myHeader = OperationContext.Current.RequestContext.RequestMessage.Headers.GetHeader("myHeader", "myNS");
Hope it helps.
Working with Visual SourceSafe always feels to me like walking on eggshells. One wrong move and you get that sickening crunching feeling and things seem to collapse.
My current project's integrator decided to keep things neat. There's a central solution containing all the projects, sitting under a central project in VSS. Dozens of projects, divided with Solution folders, all neat and ready for integration builds.
Naturally, most of us don't really need all those projects open in our solutions when we work. They slow down work and are generally a headache, so we decided that everyone can create his own customized solutions and work against them.
For some reason, the decision was made to create a project folder under VSS for each team member, and have each team-member keep his own solutions there so as to not mess up the main project folder with random SLNs. It goes something like this:
/ MainSolution / FirstProject
/ MainSolution / SecondProject
Where my SLN file now sits in $/Avner but references projects in $/MainSolution/FirstProject.
Anyone who's worked with VSS might already be wincing in anticipation.
Visual Studio doesn't really like to have project references not sitting underneath the current solution's VSS root. If I add a new project under my solution and then check it in, it will automatically check that project in under my solution folder ($/Avner/ThirdProject). I have to remember to manually go to File -> Source Control -> Add to Source Control and answer 'No' when it asks me whether to add it under the solution folder.
Assuming I made a mistake, I can either spend 20 minutes struggling with moving it around and updating the bindings, or go the easy way: Erase the projects from VSS, remove them from the solution, then re-add them, permanently remove all SCC bindings and re-add them to the proper place. Ugh.
Since no-one enjoys that, I'll try to list some recommendations:
1) When working with multiple solutions, it's best to have them all reside in the same root solution project.
2) If you must put them in alternate folders, it's best to create any new projects from the master solution to ensure they are created in the right place, then add them to the sub-solution using File->Add Existing Project -> From SourceSafe (LAN).
3) If you created them directly from the subsolution, make sure you don't check them in immediately, but manually add them to the right place from File -> Source Control -> Add Project To Source Control.
4) If you did, don't try to fix it. Just erase from VSS and from the solution and re-do from start.
A few days ago, Guy Burstein
wrote a entry on avoiding design-time generated proxies by extending the ClientBase class to create a GenericProxy. This proxy uses the client binding data in the app.config file to auto-create an appropriate proxy when the interface is shared by both client and server.
I gleefully lifted that piece of code for use in my emerging WCF project, but needed it a bit more generic than that. Seeing as I currently have no need for any fine-tuned configuration, I can get by with a GenericProxy that explicitly gets the Binding and EndpointAddress of the service in question. Since the ClientBase already supports this, I just have to make sure that my GenericProxy exposes this too:
public GenericProxy(Binding binding, EndpointAddress address)
: base(binding, address)
Which now allows me to instantiate my proxy like this:
EndpointAddress address = new EndpointAddress(serviceUri);
Binding binding = new NetTcpBinding();
GenericProxy configurationProxy = new GenericProxy(binding, address);
As long as I don't need any customizations beyond the basic binding and URI, I don't need any configuration files whatsoever in my client.
So I've started messing around with WinFX and WCF.
It certainly feels like it needs more work, especially on the dev-tool end of things.
I was trying to run SvcUtil to generate metadata and proxy from a console application and ran into this error message:
Error: There was an error exporting the ContractDescription loaded from the type
: Strawjackal.WCFTest.IService, MyService, Version=188.8.131.52, Culture=neutral, PublicKeyToken=null
Duplicate contract QNames are not supported.
Another ContractDescription with the Name: IService and Namespace:
Strawjackal.WCFTest has already been exported.
This was very confusing until I found this page here explaining that SvcUtil is simply broken when running on EXE assemblies. The original poster's solution was to recompile his assembly as a class, but I've discovered that an even simpler solution is to just rename my EXE to a DLL, run SvcUtil against it, and rename it back.
1) Documentation Errors
When reading the documentation, I get scripting errors when accessing every page – a 80041001 script error, after which the page is readable but suffers from some layout issues. Oh, and occasionally it crashes.
According to this forum discussion, this is caused by an IE7 incompatibility with some of the content in the SDK. One solution is to downgrade IE, and the other is to switch to a newer CTP release (where some of the help content has been fixed). Since I am loathe to uninstall IE7 (which rocks) and I can't upgrade the SDK to the Feb. CTP (since our project uses the January bits), I'll just bite the bullet and live with the errors, and wait for the next IE7 beta drop which may fix it.
2) Visual Studio extensions.
For some reason, after installing the SDK and the Runtime Components on my XPSP2 machine, I can't get the Visual Studio extensions to install. It pops up an error dialog saying I don't have the proper prerequisites and exits.
Resolution: Downloaded the extensions again from the Microsoft site. I suspect that the version I had wasn't matched to the Jan CTP. The error didn't indicate anything to that effect, but the filedate on it was March 2nd, so maybe it's for the Feb. CTP.
3) Merging help collections
The WinFX SDK installs a large set of help files that are accessible independently from the Start Menu folder. I was looking for a way to merge those help files with the Visual Studio Combined Collection, and ran into a wall. I found various references to the H2 help-file format and various tools to build these collections, but nothing to do this supposedly simple task.
And then I got the Developer Extensions working (see #2) and it's supposed to do the work for me. Yay!
Resolution: Developer Extensions merge the help collections. You may be required to close and maybe kill all running DEXPLORE.EXE processes. You'll probably have to close all Visual Studio instances. You might have to turn off the lights and wait in absolute silence. MSDN Library may take 3-4 years to recalculate its indexes when you start. Eventually, though, you'll be rewarded with having the WinFX documentation integrated so seamlessly into the MSDN Library that you repeat the process above again, thinking that nothing had happened. Hint: It's under .NET Development -> .NET Framework SDK -> WinFX Development.