Picking up from last time, we were going to look at consuming exceptions to possibly produce a fault message. The same machinery is used here as for the reverse conversion process. Exceptions go through an instance of FaultConverter, you can create your own FaultConverter in a custom channel to control the behavior, and we have a default FaultConverter for a variety of common exception types.
Here's the entire interface for FaultConverter, including the two methods that we used last time for consuming fault messages.
public abstract class FaultConverter{ protected FaultConverter(); public static FaultConverter GetDefaultFaultConverter(MessageVersion version); protected abstract bool OnTryCreateException(Message message, MessageFault fault, out Exception exception); protected abstract bool OnTryCreateFaultMessage(Exception exception, out Message message); public bool TryCreateException(Message message, MessageFault fault, out Exception exception); public bool TryCreateFaultMessage(Exception exception, out Message message);}
The two methods of interest for today are TryCreateFaultMessage and OnTryCreateFaultMessage. TryCreateFaultMessage takes in an exception and potentially produces a fault message. Override OnTryCreateFaultMessage to handle exceptions and return whether you were successful. Unlike TryCreateException, TryCreateFaultMessage does not reject a null exception argument. This means that you need to do the check yourself if needed. However, the output of TryCreateFaultMessage has the same level of screening as TryCreateException. If you claim that your FaultConverter handled the exception but don't provide a fault message, then we'll throw an InvalidOperationException. If your FaultConverter claims it didn't handle the exception but provides a fault message anyway, we'll again throw an InvalidOperationException.
There are four exception types that the default fault converter will handle to produce specific fault messages.
ActionMismatch and MessageHeaderException require WS-Addressing 1.0 for support. ActionNotSupportedException requires either of the versions of WS-Addressing. MustUnderstand works even if you're not using WS-Addressing.
Next time: 2006 Year in Review
After a long period of dormancy, several documents related to the SOAP 1.2 standard appeared shortly before Christmas. The first four of these documents are a second edition of the SOAP 1.2 specification that incorporate all of the errata found in previous publications.
SOAP Version 1.2 Part 0: Primer (Second Edition)
SOAP Version 1.2 Part 1: Messaging Framework (Second Edition)
SOAP Version 1.2 Part 2: Adjuncts (Second Edition)
SOAP Version 1.2 Specification Assertions and Test Collection (Second Edition)
The comment period for all of these editorial revisions runs through February 2nd.
The other document is the missing third part of SOAP that covers one-way messaging patterns.
SOAP 1.2 Part 3: One-Way MEP
I find this one pretty disappointing given the wait considering that it totally punts the issue of error handling with one-way messages. The only guidance is that one-way messages may result in a SOAP fault but nothing is explained about how to deliver or process that fault. Error handling is the hard part of one-way messaging so there's no meat to this specification without it.
The next two episodes are about consuming fault messages and exceptions. Day one covers consuming fault messages and possibly producing an exception. Day two covers consuming exceptions and possibly producing a fault message. Both directions work by going through an instance of FaultConverter. You can override this conversion behavior in a custom channel by subclassing FaultConverter and returning an instance whenever GetProperty<FaultConverter> is called on your channel. We also have a default FaultConverter that handles a number of cases.
When your channel receives a fault, there are three things you can do. If the fault message is unintelligible, you might just give up and treat the fault message as you would any other bad message. Assuming that the fault message is valid, you'll want to either handle a recoverable fault or propagate an unrecoverable fault. You'll also want to handle any fault that higher levels of the protocol stack will find unintelligible. That just leaves dealing with propagating a fault as an exception.
We'll need two methods from FaultConverter to take care of this last case.
public bool TryCreateException(Message message, MessageFault fault, out Exception exception);protected abstract bool OnTryCreateException(Message message, MessageFault fault, out Exception exception);
TryCreateException takes in a fault message and potentially produces an exception. Override OnTryCreateException to handle fault messages and return whether you were successful. If you claim that your FaultConverter handled the fault but don't provide an exception, then we'll throw an InvalidOperationException. The same thing happens if your FaultConverter claims it didn't handle the fault but provides an exception anyway.
The default fault converter can handle eight different faults in its OnTryCreateException, some of which only apply to specific addressing modes. I've talked about several of these before, but here's the complete list.
Next time: Consuming Faults, Part 2
The second most distinguished fault, with almost as many special cases and discussion as the MustUnderstand fault I talked about last week, ended up not making it into the product. Some form of the fault is still in there of course, but it lost its mouthful of a name: the InvalidCardinalityAddressing fault.
An InvalidCardinalityAddressing fault occurs when you have more or fewer copies of a header than was expected. For instance, messages are expected to have at most one MessageID. Having twelve MessageIDs would be frowned upon. On the basic MessageHeaders collection, this is checked when accessing the Action, FaultTo, From, MessageID, ReplyTo, or To properties. Detecting that there's an InvalidCardinalityAddressing problem only takes place when someone looks at the message and in particular looks at the offending header. Given our extensible protocol stack, there's no way to predict who the first person to look at a particular header will be. Therefore, this is one of the few faults that can potentially occur at any layer.
Since there's no longer an InvalidCardinalityAddressingException to use in situations like this, you should be expecting a MessageHeaderException, one of the subtypes of ProtocolException, to come back instead.
public class MessageHeaderException : ProtocolException{ public MessageHeaderException(); public MessageHeaderException(string message); protected MessageHeaderException(SerializationInfo info, StreamingContext context); public MessageHeaderException(string message, bool isDuplicate); public MessageHeaderException(string message, Exception innerException); public MessageHeaderException(string message, string headerName, string ns); public MessageHeaderException(string message, string headerName, string ns, bool isDuplicate); public MessageHeaderException(string message, string headerName, string ns, Exception innerException); public MessageHeaderException(string message, string headerName, string ns, bool isDuplicate, Exception innerException); public string HeaderName { get; } public string HeaderNamespace { get; } public bool IsDuplicate { get; }}
This exception is also generated by the standard wire faults InvalidAddressingHeader (subcode InvalidCardinality) and MessageAddressingHeaderRequired. MessageAddressingHeaderRequired translates to IsDuplicate = false while InvalidAddressingHeader.InvalidCardinality translates to IsDuplicate = true. This interpretation of duplicates is mandated by the SOAP binding in the WS-Addressing specification so I'm hoping that everyone is doing it correctly. InvalidCardinality is otherwise vague from the name about whether it means too many or too few copies so it would be easy to get this wrong. Knowing that also helps explain why using InvalidCardinalityAddressingException for both cases was so awkward.
Next time: Consuming Faults, Part 1
I watched the story about the Google SOAP Search API develop this week with some amusement because it seemed like the discussion instantly turned into one about religion rather than technology. There's plenty of technology being slung around of course, but it's mostly people getting up on their soapbox to shout about why their technology is the best. Basically, if you've ever seen a drive-by blogging argument over whether REST programming cures cancer or kills babies, the same thing is going on here. For their part, Google hasn't talked much about the motives behind the move, although it seems to me like the switch is purely business-driven. If you didn't follow the story, here's what happened.
Back in the beginning of December, Google decided to stop handing out keys to use their SOAP API and instead told everyone to use an AJAX API. The AJAX API lets Google insert advertisements into the results, which you aren't allowed to alter. There's no easy way to put advertisements into the SOAP message because the content coming back was just data without any presentation. You may have heard that Google makes a lot of money selling advertisements.
On Monday, an O'Reilly blogger picked up this story and gave a very brief summary of the reaction. This in turn spawned a story on Slashdot, which is pretty much guaranteed to cause any story to get out hand. The conversation there quickly centered on a few main points.
There may have been comments actually relevant to the story, but I'd have to put the moderation settings much lower to find them. This spilled over until Mark Lucovsky (formerly of Microsoft, now of Google) chimed in to say exactly the same lines that had already been said back when the API was shut down in the first place. Mark's post prompted Robert Scoble to join the fray and present a very confusing position about this being a Google vs. Microsoft battle. In some cases, sanity prevailed, while in other cases, not so much.
I've gotten some questions about the issue, but I don't think this really impacts us, our customers, or SOAP at all. This impacts some of Google's customers, but it mostly seems like Google is favoring the customers that pay them (by showing their advertisements) over the customers that don't.
There are no topics planned for December 25th, December 26th, or January 1st unless, by popular demand, you'd like to hear about my technique for perfectly extracting cranberry sauce from a can. Otherwise, you'll have to go and read some of the older articles to get your regular dose of WCF knowledge. January 2nd will have a 2006 Year in Review for the blog.
There is one fault that distinguishes itself from all the rest by the level of support built in for it throughout the platform. This fault has its own attribute, its own behavior, works with any transport or protocol, is one of the few faults handled by the default fault converter, and even gets a mention in the standard that defines the concept of faults.
If you haven't figured out which fault I'm talking about by now, it's the MustUnderstand fault. I've talked about the MustUnderstand message header attribute and fault code before, but I'm pretty sure I haven't talked about the behavior (MustUnderstandBehavior) or exception type (it's an internal subclass of CommunicationException, so why worry about it though?).
MustUnderstand is an exceptional fault type in the sense that it is not dealt with at a particular protocol level. The meaning of a MustUnderstand fault is that some protocol that the sender speaks has not been properly dealt with by the receiver. In some cases, the sender doesn't care that the receiver can't understand a particular protocol. However, when the sender does care, it uses the MustUnderstand property of a message header to communicate that fact.
A MustUnderstand fault must be generated above any protocol channel that could potentially handle the message header. The fault is not an application fault, so it also must be generated below the application level. In WCF, the most common layer that meets these criteria is the dispatcher. The dispatcher checks through the message header collection looking to see if any header was marked as MustUnderstand but has not been marked as processed. If so, the dispatcher throws an exception. Performing this check in the runtime is prompted by the MustUnderstand endpoint behavior. There shouldn't be anyone attempting to handle the exception, so it will eventually reach a fault converter. At the fault converter, the exception is converted to a standardized MustUnderstand fault. If you're using SOAP 1.2, then you'll also have headers added to the fault message covering all of the other known unhandled message headers. Otherwise, all that is available is the header that was first noticed to not be understood.
Next time: A Historical, Awkwardly Named Fault
Announcements- Notice that something new related to WCF has come out (primarily product releases).
Answers- Articles based on questions that I've gotten or that explain how to solve a specific problem.
Bindings- Articles about writing or using bindings, custom bindings, standard bindings, or binding elements.
Channel Extensibility- Articles about using our extensibility points for protocol and transport channels. This is a subcategory that applies to either the Channels and Transports topics.
Channels- Articles about writing or using protocol channels. If you're instead looking for information about transport channels, then the right topic is Transports.
Documentation- Articles that will eventually appear in the official product readme or SDK. These articles are usually the first draft that I've written before the content has been reviewed.
Encoders- Articles about writing or using message encoders.
Hosting- Articles about running WCF services inside of a hosting environment, such as IIS.
HTTP- Articles about the HTTP protocol or our HTTP transport channel.
Indigo- Every article that is specifically related to WCF. That's about three out of four of the daily articles. Popular topics that are more general than just WCF are Networking and Security Algorithms.
Messages- Articles about creating or using XML InfoSet messages.
Misc- A small number of articles that would otherwise not be covered by any of these topics.
Networking- Articles about general-purpose network protocols and features.
Samples- The basic idea is that you describe a scenario and then explain how to implement that scenario in a step-by-step process. These frequently are long, multi-part series with extensive code listings.
Security- Articles about networking or Windows security. Security Algorithms and Transport Security are popular subcategories that can also be applied to this topic.
Security Algorithms- Articles about general-purpose algorithms for securing data. These articles usually involve a bit of math.
Service Architecture- Articles about the design of a service application or group of services.
Standard Bindings- Articles about one of the predefined system bindings that ship with WCF.
Stream Upgrades- Articles about the extensibility point on connection-oriented transports that can modify the byte stream before transmission. This is a subcategory of the Transports topic.
TCP/IP- Articles about the TCP/IP protocol suite or our TCP transport.
TechEd- Articles about the regular Microsoft TechEd conference.
Transport Security- Articles about securing data at the network transport layer, particularly through the use of SSL/TLS security.
Transports- Articles about writing or using transport channels.
Next time: The Most Distinguished Fault
A fault code is an opaque string that classifies an error. The fault code string doesn't have to be meaningful, it could be "X", but having short yet meaningful strings can aid the debugging experience. The real meaning is in the fault reason that we talked about last time. In addition to the fault code string, there is an optional namespace for the fault code and the ability to nest another fault as a subcode. The fault subcode functions much like an inner exception.
We recognize three distinguished namespaces although you can supply any namespace string you'd like.
The namespace string is also opaque rather than a real URI so you have to exactly match these strings to be recognized as a predefined namespace. There is no equivalence between two namespaces by operating on URI path components or anything like that.
public class FaultCode{ public FaultCode(string name); public FaultCode(string name, FaultCode subCode); public FaultCode(string name, string ns); public FaultCode(string name, string ns, FaultCode subCode); public bool IsPredefinedFault { get; } public bool IsReceiverFault { get; } public bool IsSenderFault { get; } public string Name { get; } public string Namespace { get; } public FaultCode SubCode { get; } public static FaultCode CreateReceiverFaultCode(FaultCode subCode); public static FaultCode CreateReceiverFaultCode(string name, string ns); public static FaultCode CreateSenderFaultCode(FaultCode subCode); public static FaultCode CreateSenderFaultCode(string name, string ns);}
There are a few well-known fault code strings defined in the SOAP specifications: VersionMismatch, MustUnderstand, Client/Sender, Server/Receiver, and DataEncodingUnknown (SOAP 1.2 only). The confusion over Client/Sender and Server/Receiver is the reason why there are all these special methods for receiver and sender faults. Client and Server are the SOAP 1.1 names while Sender and Receiver are the SOAP 1.2 names. We'll automatically pick the right version for you based on the namespace, with the default being the SOAP 1.2 names for namespaces we don't recognize (unenveloped messages also use the SOAP 1.2 names).
Next time: Tagging Glossary
A large windstorm knocked out power to much of the Seattle area late Thursday night. Most of the city is back up although about half of the surrounding suburbs were still dark as of Sunday afternoon. The posting machine had no power at all on Friday, and although it looks like Microsoft has power again, the machine doesn't seem to have come back online. If a second post shows up Monday morning, then lucky! Otherwise, we'll continue on Tuesday.
Edit: Looks like the machine needed some help getting restarted.
Let's carry on with some discussion of the FaultCode and FaultReason classes. I was planning to do these two types together but I decided to do them as separate articles and make each of them a bit more detailed.
I'll start with FaultReason as it's the simpler of the two. The fault reason is the primary way that users understand what happened when the fault occurred. You'll notice that FaultReason references a FaultReasonText type, which is so simple that I'm not going to break out a code section for it. A FaultReasonText is just an association of a text string with a cultural identifier. We'll see how that combination comes into play for localization of fault descriptions.
public class FaultReason{ public FaultReason(FaultReasonText translation); public FaultReason(IEnumerable<FaultReasonText> translations); public FaultReason(string text); public SynchronizedReadOnlyCollection<FaultReasonText> Translations { get; } public FaultReasonText GetMatchingTranslation(); public FaultReasonText GetMatchingTranslation(CultureInfo cultureInfo); public override string ToString();}
A FaultReason is a concept that's independent of the actual words used to describe the fault. You can supply a number of different text strings that get picked from depending on the user's language settings. The Translations bucket holds all of the different text strings and their associated cultural identifiers (tied together by a FaultReasonText). When no culture is specified for a fault reason or a translation search, the assumed culture is the current thread culture. The translation process works like this:
For example, if you want a translation to "en-UK", we'll first look for "en-UK" and then we'll look for "en". If we still can't find a match, then we'll take the first translation in the list, which could be anything.
Next time: Creating Faults, Part 3
I promised to start going over the object model for faults today, and I've split that coverage into two parts. The first part highlights the MessageFault class and creating fault messages (yes, it's a bit confusing that those are separate things). The second part goes over the FaultCode and FaultReason classes. I'm actually leaving quite a bit of detail out because there are a lot of moving parts involved in fault messages, many of which won't make sense until we get farther in.
A fault message consists of a standard message whose body is represented by the MessageFault class. There are several static overloads on the base Message class that are used to create fault messages. The overloads give you various combinations of the fault code, fault reason, and fault detail that I talked about last time. No matter which option you choose, all of these code paths eventually end up creating a message with an XML body writer that is generated from a MessageFault.
public static Message CreateMessage(MessageVersion version, MessageFault fault, string action);public static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, string action);public static Message CreateMessage(MessageVersion version, FaultCode faultCode, string reason, object detail, string action);
The Message is just a carrier, which means that everything interesting is on the MessageFault class.
public abstract class MessageFault{ protected MessageFault(); public virtual string Actor { get; } public abstract FaultCode Code { get; } public abstract bool HasDetail { get; } public bool IsMustUnderstandFault { get; } public virtual string Node { get; } public abstract FaultReason Reason { get; } public static MessageFault CreateFault(FaultCode code, FaultReason reason); public static MessageFault CreateFault(FaultCode code, string reason); public static MessageFault CreateFault(Message message, int maxBufferSize); public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail); public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer); public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor); public static MessageFault CreateFault(FaultCode code, FaultReason reason, object detail, XmlObjectSerializer serializer, string actor, string node); public T GetDetail<T>(); public T GetDetail<T>(XmlObjectSerializer serializer); public XmlDictionaryReader GetReaderAtDetailContents(); protected virtual XmlDictionaryReader OnGetReaderAtDetailContents(); protected virtual void OnWriteDetail(XmlDictionaryWriter writer, EnvelopeVersion version); protected abstract void OnWriteDetailContents(XmlDictionaryWriter writer); protected virtual void OnWriteStartDetail(XmlDictionaryWriter writer, EnvelopeVersion version); public static bool WasHeaderNotUnderstood(MessageHeaders headers, string name, string ns); public void WriteTo(XmlDictionaryWriter writer, EnvelopeVersion version); public void WriteTo(XmlWriter writer, EnvelopeVersion version);}
You should be able to immediately pick out how to access the fault code, fault reason, and fault detail given what's been covered so far. The fault detail is available as either a strongly typed object or the underlying XML stream. Accessing the detail as a strongly typed object is just a convenience. Under the covers, everything is going through the XML reader. The only thing that you're saving by using typed objects is potentially typing lines of code.
The serializer that keeps popping up in the methods here is the one that gets applied to the detail object. We do have a default serializer that is used if you do not supply your own. The only difference between the default serializer for a fault detail and the default serializer for other messages is that in the fault detail case, the MaxItemsInObjectGraph setting is cranked way up.
Next time: Creating Faults, Part 2
A WCF message exchange session consists of three types of messages.
I'll be talking about fault messages for the next two weeks. This first post is an introduction to some of the terminology used to describe faults in a SOAP messaging system. Even though there are differences in fault handling between SOAP 1.1 and SOAP 1.2, I'm not going to belabor that point. I will mostly be referring to SOAP 1.2 fault messages and you can translate those concepts into other messaging systems.
Faults come with a code that lets you programmatically determine the type of the fault. Fault codes sound a lot like error codes but are actually implemented using the model of exception types. The representation of fault codes consist of opaque strings (much like an exception namespace and type name). Creating new fault codes should be a lot like creating new exception types. It's unnecessary to create a new code for every possible fault. Instead, a new code is needed only when the client is expected to programmatically handle the fault differently than other types of fault.
The differentiator between multiple faults using the same fault code is often the fault reason. A fault reason is the human readable description of the fault. Users are unlikely to make a decision based on the fault code. Anything that could have been figured out from the fault code should have been performed automatically. Instead, users are going to read this description to understand what corrective action they should take.
The last part of a fault is the fault detail. Fault details are arbitrary content attached to the fault that the client may be able to make use of. Interpretation of the fault detail is often only possible if you know something about the particular system that generated the fault. Otherwise, the fault detail is essentially a blob that lives inside of the fault.
These three elements are part of the WCF object model for faults. Put together, these elements form the bulk of the body of a fault message. I'm going to cover the object model in more detail starting next time.
Next time: Creating Faults, Part 1
The idea of metadata is to provide a description of your service such that someone, with no other information about you or your service, can come along and build a client. If the metadata has sufficient detail, then the client and service can intelligibly exchange messages with each other. That doesn't mean the two sides can communicate. The client may have no idea what the server's Zykwygzle method does. However, it's a start.
Even though metadata is very useful in this way, that doesn't mean that you want to share your description with just anyone. In the same way that error pages started on by default and later got turned off, metadata has long been on by default but was disabled during the beta. Again like error pages, there's a certain amount of potentially sensitive information in the metadata that you might not like an attacker to have. If you're comfortable with revealing the service metadata, then you can enable the metadata provider. I've already talked about how to flip the switch that turns metadata back on.
Enabling metadata adds another endpoint to your service that supports the IMetadataExchange service contract. This endpoint is an infrastructure endpoint, meaning that while it supplies a web service, its service is in a supporting role to another endpoint. Since metadata is available on its own endpoint, you can supply any binding you choose for serving this content. There are two popular configurations.
The first configuration is to restrict the metadata to exactly the same group of people that have access to the service. You probably have everything you need to take care of this because you've already defined a web service with that level of protection.
The second configuration, and probably the more popular one, is to simply make the metadata widely available regardless of the level of protection of your service. In this case it seems like you need a configuration that's totally separate from the service. However, we can do most of the work for you. For instance, suppose you've got an HTTPS service hosted in IIS at https://mydomain.com/service. You've had to create an SSL certificate, configure IIS, and setup the security in the service configuration. Releasing the metadata to the world is still really simple. You need to add a single behavior to the configuration.
<serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
Now, you have automatically generated service metadata available at http://mydomain.com/service?wsdl that anyone can come up and consume.
Next time: Basics of Failure
One of the things I've been meaning to do since the reader poll is to organize all of the past posts into some kind of a table of contents. While I haven't quite figured out how to do that yet, I've started working on a few things now to help the organization of posts. Over the next few weeks, I'll be using a few of the daily post slots to roll out some of the improvements as they become available. The first one, which should hopefully be available next week, is a guide to what all of the various category tags mean.
The story from yesterday: there is an important setting for composite duplex that is only settable through the binding context. Unfortunately, proxy clients automatically create and use their own binding context so there is no convenient time to poke in a replacement value for the setting. What kind of a workaround can we come up with?
Since there is no convenient way to intercept the binding context at the producer, we'll instead need to intercept the binding context at the consumer. The consumer of the binding context is the channel construction process of the binding. In particular, we need to modify the ListenUri setting before we reach the BuildChannelListener method of the composite duplex binding element. That means our opportunities for interception are going to be in methods such as CanBuildChannelFactory, CanBuildChannelListener, BuildChannelFactory, and BuildChannelListener. The binding context instance gets passed along from binding element to binding element so it is safe to modify the setting at any opportunity along the way. We don't have to worry about finding the correct instance of the binding context. Every instance that we see is one that we want to modify.
The solution that I came up with for this problem was to write a binding element that modified the ListenUri as the binding context went by. Other than this change, the binding element simply delegates every method to the next binding element down. You can position the binding element in the channel stack so that your customization is always reached before the composite duplex binding element. The binding context that composite duplex receives contains your modifications, giving you full control over the ListenUri setting that was otherwise hidden. This solution is generally reusable for other situations where you need to modify the binding context or channel construction process. I won't say that there are many such situations, but it is a tool for you to use.
An interesting coincidence about this problem was that two different people on consecutive days with different scenarios were looking for a solution. Mike Taulty was the second person and he's done a write up of why he was interested in composite duplex and the code I gave him for the ListenUri modifying binding element. Mike added configuration support, which I hadn't bothered with. From configuration, you'll need to build out a complete custom binding. From code, it's simpler because you can start with an existing binding and simply insert the binding element into the binding element collection at the top of the channel stack. However, configuration makes it much easier to just drop this into a working program.
Next time: Unprotected Metadata
The ListenUri is a three-part setting on the BindingContext that gives the server-side address of a connection. The ListenUri has a base and a relative address, plus a mode switch. The mode switch controls whether the uri should be used as-is or whether parts of the uri have been left unspecified and need to be automatically generated. A common automatic address generation scenario is picking an unallocated network port.
There are only a few places where the ListenUri is actually used. One spot is in the transport, which needs to construct a uri for the remote address. The spot that I'll actually talk about though is the composite duplex channel. The composite duplex client channel uses the ListenUri for the backchannel address. I've already covered the basics of composite duplex and how the ListenUri factors into the backchannel construction process.
One of the interesting things about the composite duplex channel is that it has a partial fallback for the ListenUri, described in the article above. The ClientBaseAddress setting on the composite duplex binding element lets you specify a base address if the ListenUri is not provided. However, there's no equivalent ClientRelativeAddress that lets you complete the ListenUri. Therefore, if you use this fallback, then you'll always get some randomized component in your backchannel address. You generally want a randomized address because the backchannel address has to be unique across all of your current composite duplex clients. Having a randomized address doesn't work though when you need to allocate a resource with a particular name for the backchannel. MSMQ is an example where randomized addresses don't work because you need to allocate a queue whose name is tied to the backchannel address.
When you're programming against the channel model, working around this situation is not difficult because you own creating the binding context. This means that you have an opportunity to directly set the ListenUri. If you are using an automatically generated proxy client like most people do, then you have no opportunity to set the ListenUri. The proxy takes the configured binding and instantiates a copy, including the binding context, automatically. This is entirely hidden from the proxy client user. There are mechanisms for passing information to the channel construction process, called binding and channel parameters, but there is no binding parameter that corresponds to the ListenUri on the binding context.
With no way for the proxy client user to access the binding context or ListenUri, how do you use composite duplex with a non-randomized address? It turns out that you need to be creative intercepting the binding context before the composite duplex binding element gets a chance to see it.
Next time: ListenUriBindingElement
This was a bug that we found a few weeks before V1 shipped but it was not deemed severe enough to fix at the time. I know that one customer has hit this while experimenting and it's pretty easy to understand what's going wrong once you hear the details.
The bug affects the use of the reliable messaging channel with different message addressing versions. The reliable messaging channel captures a copy of the first set of headers it sees used for addressing in the application domain. Later, the channel replays those same headers any time it needs to use addressing again. If you have a second endpoint that creates a reliable messaging channel, then that channel will also use the cached headers. However, the other endpoint has a different binding and could be using a different version of message addressing. Having the wrong version of addressing headers typically results in a protocol fault complaining about the version mismatch.
It seems unlikely that this would block development of a real application. You have to be using a custom binding to hit this because all of our standard bindings that support reliable messaging use the same addressing version. The workaround is that if you need to have multiple service endpoints that both use reliable messaging but have different message addressing versions, then you should create a new application domain to keep the endpoints separate.
Next time: Manually Injecting a ListenUri
How do I override the default configuration file?
There's a number of different answers to this question depending on what you really intend to do with the configuration. Most of the time, the answer is a little harder than you would expect unless you've worked with configuration in the past.
The default configuration file for your application is based on the executable name and location. If what you want to do is have your application use a different configuration file, then that is mostly straightforward. You can create your own application domain and use the AppDomainSetup to point at the desired application configuration file. Your service runs in that application domain and gets the right configuration.
Next, you may want your application to use the standard configuration file but override some of the settings. There are two options for this. The first option is to override ApplyConfiguration in ServiceHost. After calling the base ApplyConfiguration method, you have the default configuration loaded. You can do whatever additional processing you want at that point. The second option is to rewrite the configuration using the ConfigurationManager class. This is a part of the standard configuration package and doesn't know anything about WCF. That means you have to be knowledgeable yourself in how WCF configuration is laid out.
Finally, you may want your application to not use a file-based configuration system at all. For instance, your configuration may be stored in a database or dynamically generated. In this case, you still need to override ApplyConfiguration, but this time you're not going to call the base ApplyConfiguration method. Instead, you have to apply the configuration from scratch using the external source. I don't know of an easy way to parse a standard WCF configuration file in memory for your custom ApplyConfiguration method. If you can write a temporary configuration file to disk, then you can alternatively just use the first method of creating a new application domain. Otherwise, you need to have a parser for your external configuration file format.
Next time: Incompatible Addressing with Reliable Messaging
HTTP namespace sharing allows you to build different pieces of your web site using different technologies. For instance, some parts of your content may be static pages or ASP.NET pages while other parts of your content may be web services. It would be really inconvenient if each technology had to be run in its own server on a different port or with a different host name. These sorts of things pretty much work out of the box now thanks to some improvements made in Windows Server 2003. Except when they suddenly stop working, which is really frustrating.
Here's an average example of namespace sharing. IIS has grabbed the root path and some of its content is off in a subpath. There's a WCF service running in a different subpath and that service may not even be hosted in IIS. For instance, it could just be a standalone web service always running on the machine. The WCF service has a couple of endpoints and all of this configures nicely.
The problem starts when you look at the different ways you could have configured that WCF service. If you have a base address of S1 for the service host, then the service endpoint is at the service root path and the metadata endpoint is in a subpath. This works fine. Then, you change the configuration so that the base address is the root path. The service endpoint is at the S1 subpath and the metadata endpoint is in a subpath farther down. Now, everything breaks.
What has happened is that WCF makes a reservation for the base address of the service so that it can see all of the requests coming in to that part of the namespace. However, I told you way back at the beginning that IIS has already grabbed the root path for its own purposes. That's not going to work. Actually, it does work because WCF takes the namespace more forcefully than IIS, but either way some part of your web site is now hosed. This is why it actually matters how you specify addresses in terms of base and relative paths. The same thing can happen between two WCF services even when IIS is not in the picture. However, in that case you'd get an exception starting the second service because they're both fighting equally hard for the reservation.
Next time: Overriding the Default Configuration File