Nicholas Allen's Indigo Blog

Windows Communication Foundation From the Inside

August, 2007

  • Nicholas Allen's Indigo Blog

    Number of Connections for Secure Conversation

    • 1 Comments

    Why does a service call require creating multiple connections when using message security?

    Several common uses of message security require sending multiple messages, particularly on the first call for a given proxy. For example, one way to establish a security context is by making use of a token service. The client first connects to a token server to request a security token. The token server responds with a security token. Then, the client connects to the original server and passes in the security context token. These requests may be accompanied by multiple rounds of challenges that require sending additional messages. HTTP is particularly susceptible to requiring multiple connections in a scenario like this but other transports can require multiple connections as well because more than one server may need to be contacted.

    The important point though is that the bulk of the work happens with the first call for a given proxy. If you open and close proxies frequently, then you'll pay this cost many times. If you reuse proxies appropriately, then you'll reduce the number of messages required and hopefully the number of connections required as well.

    Next time: Metadata is Locked

  • Nicholas Allen's Indigo Blog

    Semantic Annotations for WSDL

    • 0 Comments

    The other day the W3C released a new recommendation called Semantic Annotations for WSDL and XML Schema. Semantic annotations are used to connect an element in a structural model to an element in a semantic model. The semantic model element describes the meaning or purpose of the structural element when it appears in a document. Most message processors only care about the document structure but a human reader cares about the document meaning as well. There are many different ways that meaning can be recorded. This standard just provides a way to construct the connections.

    I've never had anyone ask for a feature like this so I'd be curious to know how you'd want to use it. I could see some benefit to annotating a business process or an application entry point with information about the meaning of the exchanged messages. Originally, these systems had both their structure and semantics described by text documents. There has been standardization for describing structure but the semantics have remained in those text documents. It's interesting to be able to recouple the two together so that they don't get separated.

  • Nicholas Allen's Indigo Blog

    Attribute Driven Transactions

    • 1 Comments

    How are transactions controlled by decorating attributes on service methods?

    There are three important transaction-related attributes that can be supplied through operation behaviors on the service implementation.

    1. By setting the TransactionScopeRequired setting to true, all of the code in the service operation implementation runs inside of a TransactionScope. Without a transaction scope, the other attributes described here aren't very interesting.
    2. The TransactionAutoComplete setting controls whether the transaction automatically completes at the end of the service operation. The transaction will only be completed if the operation was successful and didn't throw an exception. An exception causes the transaction to be aborted.
    3. The TransactionFlowOption setting controls whether the transaction scope is a new transaction or is a transaction that comes from the caller. There are three options to distinguish the case where the caller might provide a transaction from the case where the caller must provide a transaction.

    Next time: Number of Connections for Secure Conversation

  • Nicholas Allen's Indigo Blog

    MSMQ Addressing

    • 3 Comments

    Here's a quick guide to how an address that uses the net.msmq scheme in WCF gets turned into an actual MSMQ address. There are three transfer protocols used with MSMQ: native, SRMP (SOAP Reliable Messaging Protocol), and SRMPS (SOAP Reliable Messaging Protocol Secure). Each of the transfer protocols translates addresses slightly differently.

    For a given net.msmq address, we'll first check the transfer protocol. If the transfer protocol is SRMP or SRMPS, then the MSMQ address is going to be a direct address. The only difference between SRMP and SRMPS is that SRMP uses the http scheme while SRMPS uses the https scheme. The MSMQ address produced from net.msmq://hostname:port/queuename is DIRECT=http://hostname:port/msmq/queuename. Having a port is optional, and this part of the address is omitted if no port is specified. A private queue name is written as private/queuename in net.msmq but translates to private$/queuename in the MSMQ address. It's illegal to include the dollar sign in a net.msmq address.

    If the transfer protocol is native, then the MSMQ address is either going to be a GUID address or a direct address. A GUID address is created if the binding has been configured with UseActiveDirectory set to true. The GUID uniquely identifies the queue. Otherwise, the MSMQ address produced from net.msmq://hostname/queuename is DIRECT=OS:hostname\queuename. Unlike SRMP, a port is never specified with the native transfer protocol. Again, a private queue name is written as private/queuename in net.msmq but translates to private$\queuename in the MSMQ address. Finally, if the hostname is an IP address, then the resulting MSMQ address instead is DIRECT=TCP:hostname\queuename.

    Next time: Attribute Driven Transactions

  • Nicholas Allen's Indigo Blog

    Endpoints with Multiple Schemes

    • 1 Comments

    My service has two endpoints, an HTTP endpoint for serving metadata and a non-HTTP endpoint for the actual service. The base address of the service is the non-HTTP endpoint. How do I get the HTTP endpoint to work? I get an error even if the metadata address is fully qualified.

    The error in this case was that the only known scheme was the one used by the non-HTTP endpoint. Adding an HTTP endpoint failed because http was not in the list of base addresses. You need to use relative and absolute addresses at least somewhat consistently to create a valid configuration. If you have no base addresses and all of your endpoints use absolute addresses, then everything works fine. If you have a base address, then make sure that you have a base address for every scheme that you plan to use. You can still mix relative and absolute addresses for the actual endpoints as much as you want.

    Next time: MSMQ Addressing

  • Nicholas Allen's Indigo Blog

    Measuring Reliable Session Speed with Duplex

    • 3 Comments

    The results last time were in disagreement with the rule of thumb that turning on reliable messaging cuts your throughput roughly by half. One possible explanation for the disagreement is that the measurements were looking at the wrong thing.

    The measurement being used was efficiency: the ratio of the throughput with reliable messaging enabled to the throughput with reliable messaging disabled. For all but the smallest sizes of reliable sessions, reliable messaging over HTTP had an efficiency of at least 0.5. The largest size of reliable session had efficiency near 0.9. For the rule of thumb to be true, you'd expect to see most sizes of reliable sessions have efficiency near 0.5.

    HTTP has a datagram request-reply message exchange pattern. Do the same results occur if we have a connection-oriented transport instead of a datagram transport? I reran the tests using TCP with a binary encoding rather than HTTP with a text encoding.

    You can see with this arrangement that efficiency is between 0.4 and 0.6 for all but the smallest sizes of reliable sessions. Even using a very large reliable session does not significantly change the efficiency. These results fit very well with the rule of thumb. This only reinforces the point that measurements are only significant when taken against the application being optimized. Any change to how the service works can greatly affect the results.

    Next time: Endpoints with Multiple Schemes

  • Nicholas Allen's Indigo Blog

    Orcas Beta 2 Samples

    • 5 Comments

    When Orcas Beta 2 was released, all of the links for samples were still pointing to the Beta 1 SDK download for a few weeks. That's evidently changed recently as there is now a download available for the Orcas Beta 2 SDK samples.

  • Nicholas Allen's Indigo Blog

    Measuring Reliable Session Speed

    • 1 Comments

    What kind of a performance impact does ensuring reliable delivery have? A common rule of thumb that I've heard circulated in the past is that turning on a feature such as reliable messaging or security cuts your throughput roughly by half. I decided to put this rule of thumb to a test by taking some measurements. I instrumented a simple service to collect data over the course of many calls. I used HTTP. I used custom bindings to match up exactly the same settings both with and without reliable sessions. All of this is completely irrelevant because your service has different message sizes, different processing times, a different network, or what have you. Your service simply won't have the same numbers.

    Nevertheless, I looked at the data and decided how to best organize it. The striking feature was how much (or in some cases how little) the throughput impact changed when I varied the number of messages in each reliable session. Rather than give you numbers with unhelpful units, I created a unitless measure by dividing the throughput with reliable messaging on by the throughput with reliable messaging off.

    As you can see, the throughput was roughly cut to a third when the sessions were ridiculously small and recovered to around 90% when the sessions were ridiculously large. I think that for reasonably sized sessions though, a number between 50 and 75 percent would not be out of the question. The rule of thumb seems pessimistic.

    These measurements were taken under conditions where none of the packets were actually dropped of course. The efficiency gets worse if you need to retransmit something. However, it's meaningless to consider dropped packets when one part of the measurement is non-reliable transfer. Non-reliable transfer doesn't work when packets are dropped. You can make something extremely fast if it doesn't need to be correct.

    Next time: Measuring Reliable Session Speed with Duplex

  • Nicholas Allen's Indigo Blog

    Faking Poison Message Handling

    • 3 Comments

    MSMQ has gotten a bit of an upgrade in the latest releases, particular in the area of poison message handling. Let's look a bit though at what users of Windows Server 2003 can do in the meantime before the next version of Windows Server is officially available. The interesting control to look at for poison message handling is the set of actions that are available when a message is discovered to be poisonous. MSMQ 4 has four options for handling these messages:

    • The Fault option causes an MsmqPosionMessageException to be thrown and the listening service stops processing messages. The offending message is left in the original queue.
    • The Drop option causes the offending message to be discarded.
    • The Reject option causes a rejection notice to be sent back to the submitter. The offending message is removed from the queue.
    • The Move option causes the offending message to be moved from the original queue to a separate poison queue.

    If you don't want to lose messages and you don't want your service to shut down, then the most desirable option is to move the offending message to some other queue. Unfortunately, the Reject and Move options are not available for MSMQ 3. Your only choices are to fault the listening service or drop the message.

    However, you can use the Fault option with WCF extensibility to fake more sophisticated poison message handling strategies at some cost to speed. The essence of the trick is to use the fault notification of the listening service to perform some corrective action. For example, here's the strategy that you would use to replicate the move option.

    1. Register an IErrorHandler to catch instances of MsmqPoisonMessageException as they occur.
    2. When a poison message is encountered, the service is shut down and your error handler is called.
    3. Use the message identifier in the exception to find the offending message in the original queue and move it to a separate queue that you've previously created.
    4. At the end of your error handler, restart the service if you were able to move the message.

    The third step can be replaced with whatever action you'd like to customize your poison message handling behavior.

    Next time: Measuring Reliable Session Speed

  • Nicholas Allen's Indigo Blog

    Trace Transfer

    • 1 Comments

    A few days ago I talked about how to enable tracing across service boundaries. The brief summary of that story is that tracing across boundaries works by associating an identifier with each activity and using the identifier to later reconstruct the original sequence. Today I'll talk a little bit about the implementation of the identifier. The interesting case to talk about is what takes place when we want to hand off control from an activity running on one machine to any of the following:

    • A different activity running on the same machine
    • The same activity running on a different machine
    • A different activity running on a different machine

    The mechanism for doing the transfer from activity A to activity B is a simple interlocked process.

    1. The ambient activity starts out as activity A.
    2. A TraceTransfer is generated with the identifier for activity B.
    3. If the call is blocking, then a TraceEvent is sent to suspend activity A.
    4. The ambient activity becomes activity B.
    5. A TraceEvent is sent to start activity B.

    The process happens in reverse if we want to transfer back from activity B to activity A.

    1. The ambient activity starts out as activity B.
    2. A TraceTransfer is generated with the identifier for activity A.
    3. A TraceEvent is sent to stop activity B.
    4. The ambient activity becomes activity A.
    5. If the call was blocking, then a TraceEvent is sent to resume activity A.

    Next time: Faking Poison Message Handling

  • Nicholas Allen's Indigo Blog

    Supporting Multiple Security Mechanisms

    • 3 Comments

    How do I write a service that gives clients the option to choose between different security mechanisms for protecting a service call? For example, how can I allow clients to choose between certificates and passwords?

    I think that if the example choice had been between message security and transport security, then many people would have immediately suggested having two bindings for the different security mechanisms hosted on two different endpoints of the same service. There's no reason why you couldn't use the same strategy in this case where the alternatives are two different kinds of message security. The binding configuration process involves a series of choices, including choosing from enumerations of security mechanisms. It's difficult to craft configurations that accept a wide range of valid formats at the same time.

    This choice of configurations can of course also be made less apparent by moving the choice farther away from the service endpoint. If you create an abstraction by defining an intermediate credential type, then the service endpoint is simplified by only accepting the intermediate credentials and the choice is offered by giving multiple mechanisms to obtain those intermediate credentials.

    Next time: Trace Transfer

  • Nicholas Allen's Indigo Blog

    Security and Streaming

    • 1 Comments

    Can I secure a message without having to buffer the message in memory?

    The answer to this question is yes and no, depending on what the word secure is supposed to mean. There are differences between the operation of transport-level security and message-level security, as well as potentially differences between particular security algorithms.

    Transport level security algorithms have historically composed very well with streaming. SSL does an initial handshake to exchange security information and then sets up a security session between the two parties. The security session doesn't place any significant requirements on having an available buffer of message content and the session lasts until one of the parties decides to disconnect or renegotiate the connection.

    Message level security algorithms generally don't attempt to optimize for transmission efficiency as compared to transport level security algorithms. The signing and encryption facilities of WS-Security require an examination of the complete message contents before the message can be secured. This obviously does not compose with streaming although it doesn't particularly impact pseudo-streaming approaches, such as chunking. Message level security provides capabilities not found in transport level security, such as protection over multiple hops, but these features do not come for free in terms of performance.

    Next time: Supporting Multiple Security Mechanisms

  • Nicholas Allen's Indigo Blog

    XML Support

    • 4 Comments

    What kinds of XML do messages support?

    This isn't a simple question because, like many parts of a framework, it's important to distinguish what could possibly be done given enough time versus what could easily be done given the components that ship in the box. With the components that ship in the box, you can read and write XML 1.0 documents plus the binary XML format that we include as well. All of this support is through classes for defining XML readers and writers. You can define your own readers and writers with this model. For example, it should be possible to build a message implementation that supports XML 1.1 documents, although I have never tried this to see if it would work. You can even work with documents that are not recognizable as XML in any form using the same approach as long as there is some way to project that external document to an XML InfoSet temporarily during message processing.

    Next time: Security and Streaming

  • Nicholas Allen's Indigo Blog

    Creating Sessions over HTTP

    • 5 Comments

    I've got a sessionful contract that I want to use with HTTP. How do I get the HTTP transport to produce a sessionful channel shape?

    The basic design principle of channels is that they produce whatever channel shape is their natural message exchange pattern. For HTTP, the natural message exchange pattern is request-reply. This means that if you want any other channel shape, then you need to apply a layered channel that changes the message exchange pattern. That is the approach regardless of whether you want to change the channel shape to one-way, duplex, or a sessionful channel.

    There are no built-in HTTP specific additions to create sessionful channels. We have a sample channel that demonstrates creating a session based on HTTP cookies. There are several general-purpose protocol channels that provide sessions, such as security and reliable messaging.

    However, this entire line of conversation tends to indicate a fundamental flaw in thinking. A session has the semantic meaning of correlating messages together according to some principle of relationship. Sessionful services use the relationship to treat the session of messages as a connected unit. The meaning of that session ought to be a more significant factor than whether some contract has been previously declared to know about sessions. If you are scrounging around to come up with any possible session to get a service working, then something is probably wrong.

    Next time: XML Support

  • Nicholas Allen's Indigo Blog

    Service Contract Generation

    • 1 Comments

    In the post on custom namespaces a few days ago, I used svcutil to generate the service WSDL and schema description. This use of svcutil is apparently not as well understood so I thought I'd give some more details.

    The typical use of these programs is to generate the necessary client files, proxy code and configuration, from an existing service. Rather than generating the client files directly, the same programs can be used to save a copy of the service description. Having the service description decouples downloading the metadata from generating the client files. If you would normally use svcutil [service-address] to generate client files, then you would instead use svcutil /t:metadata [service-address] to generate the metadata files. The followup command would be to run svcutil *.wsdl *.xsd to collect all of the generated metadata files and create the client files.

    Another option that exists is to run svcutil against local files rather than a running service. You'll get some of the metadata from svcutil /t:metadata [files]. However, this misses collecting information about the binding or the actual service class (as opposed to the service interface type). Processing this service description still gets you something similar but not entirely like the generated client files you would have gotten from the running service. The missing information can be supplied by a reference to the configuration. Using the /serviceName option with the name of the configuration section that defines the service, svcutil will be able to complete the service description. The configuration file that gets searched is the standard one based on the executable file name. It's important to remember that the service name is not actually the name of the service but rather the name of the service section in the configuration file.

    Next time: Creating Sessions over HTTP

  • Nicholas Allen's Indigo Blog

    Serializing UniqueId

    • 1 Comments

    Why can't UniqueId be serialized?

    Data contracts only have native support for a limited set of types. If you use a type that is not in this native set, then you'll get an exception that the data contract is invalid unless you decorate the type with attributes that explain how the type should be serialized. Adding attributes requires you to change the type, which means that there are going to be many types in the world that can't be used with data contracts. This includes types in the framework.

    System.Xml.UniqueId is an example of a type that lacks native support in the data contract implementation. It doesn't mean that there's anything wrong with that type, just that it can't be used with the data contract serialization mechanism. Other serializers will still work with the UniqueId type. For instance, XmlDictionaryWriter has native support to turn a UniqueId into a string, so the UniqueId type can be used with XML serialization.

    Next time: Service Contract Generation

  • Nicholas Allen's Indigo Blog

    Calling Services Without a Contract

    • 4 Comments

    Contracts are largely an illusion about the kinds of messages that can be exchanged between a client and a server. From a practical perspective, a service can describe itself using any contract it likes but the true measure of compatibility comes from being able to exchange data. The easiest way to experience this observation is to build and send some messages without using any contracts.

    I've talked a lot in the past about sending messages directly using channels, which are a very thin wrapper around the semantics of sending and receiving. You don't have to go all the way to using channels to experience contract-less programming though. The standard ChannelFactory class for building proxy objects has a limited built-in knowledge about the IRequestChannel shape. In essence, this is a way to send messages in a contract-oriented programming model without actually using contracts.

    Quite a few systems require sending and receiving messages but lack any support for dealing with contracts. It can be difficult to acclimate yourself to this style of programming, but it can be done to a large degree without having to give up the conveniences of a nice programming model.

    Next time: Serializing UniqueId

  • Nicholas Allen's Indigo Blog

    Tracing Across Services

    • 4 Comments

    How can I put together a stack trace that goes between service operations on different services?

    In a normal stack trace, all of the stack frames have a single observer that can view the sequence of calls and recreate them in the proper order. Some stack traces, even though they are located on a single machine, have segments that are separated by boundaries. These boundaries require multiple observers to work together to recreate the sequence. Distributed systems frequently encounter this type of boundary and there is a particular challenge getting the multiple observers to work together.

    One technique for coordinating multiple observers is to push identifiers along with the calls that allow the original sequence to be reconstructed at a later time. The identifiers help thread together the call stacks created by the individual observers.

    Service activities can be traced both with and without the capability to allow reconstruction across the boundary between two observers. Putting together a meaningful trace that spans services can be done by adding two options to the normal tracing configuration. The first option enables tracing activities. The second option enables propagating those activity traces between services. Here's an example of the configuration that would be written to enable these two options plus the required boilerplate. I've included a default listener but that is just where you'd put in whatever trace listener you're using.

    <configuration>
    <system.diagnostics>
    <sources>
    <source name="System.ServiceModel" switchValue="Warning,ActivityTracing" propagateActivity="true">
    <listeners>
    <add type="System.Diagnostics.DefaultTraceListener" name="Default">
    <filter type="" />
    </add>
    </listeners>
    </source>
    </sources>
    </system.diagnostics>
    </configuration>

    Next time: Calling Services Without a Contract

  • Nicholas Allen's Indigo Blog

    Increasing the Maximum Fault Size

    • 1 Comments

    When the service sends a fault message with a large detail, my client is unable to read the fault. Changing the standard settings for the maximum message size doesn't help. How can I read large fault messages?

    Fault messages have their own special quota that can be configured on the client proxy. For changing the settings of a proxy, you should immediately think about using a behavior. Luckily, that happens to work in this case. There is a MaxFaultSize property on the ClientRuntime, which we can get access to by supplying an IEndpointBehavior.

    public class SetMaxFaultSizeBehavior : IEndpointBehavior
    {
    int size;

    public SetMaxFaultSizeBehavior(int size)
    {
    this.size = size;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    clientRuntime.MaxFaultSize = size;
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
    }

    Let's use our new behavior to play with fault sizes. I've got a service that sends back a fault with no content, but we still have to deal with however big the rest of the fault message is. Then, the client will try connecting to the service with different maximum fault sizes to see what sizes work and what sizes don't.

    [ServiceContract]
    public interface IService
    {
    [OperationContract]
    void Operation();
    }

    public class Service : IService
    {
    public void Operation()
    {
    throw new FaultException<string>("");
    }
    }

    class Program
    {
    static void Main(string[] args)
    {
    Binding binding = new BasicHttpBinding();
    string uri = "http://localhost:8000/";
    ServiceHost host = new ServiceHost(typeof(Service));
    host.AddServiceEndpoint(typeof(IService), new BasicHttpBinding(), uri);
    host.Open();

    int maxFaultSize = 1;
    while (true)
    {
    ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
    factory.Endpoint.Behaviors.Add(new SetMaxFaultSizeBehavior(maxFaultSize));
    factory.Open();
    IService proxy = factory.CreateChannel();
    try
    {
    proxy.Operation();
    }
    catch (FaultException fault)
    {
    Console.WriteLine("Received fault with maxFaultSize={0}.", maxFaultSize);
    break;
    }
    catch (QuotaExceededException exception)
    {
    Console.WriteLine("Exceeded quota with maxFaultSize={0}.", maxFaultSize);
    }
    finally
    {
    factory.Close();
    }
    maxFaultSize++;
    }
    host.Close();

    Console.ReadLine();
    }
    }

    If we run the program, the last few lines are of interest.

    Exceeded quota with maxFaultSize=65.
    Exceeded quota with maxFaultSize=66.
    Exceeded quota with maxFaultSize=67.
    Exceeded quota with maxFaultSize=68.
    Exceeded quota with maxFaultSize=69.
    Exceeded quota with maxFaultSize=70.
    Received fault with maxFaultSize=71.

    Evidently, our empty fault takes 71 bytes to transmit.

    Next time: Tracing Across Services

  • Nicholas Allen's Indigo Blog

    Custom Namespaces

    • 7 Comments

    If you've ever looked at a generated WSDL file, you may be wondering how all of the different parts of the service description are reassembled to form WSDL. Today's article is about the namespaces in the WSDL file. Here's an example service that provides a unique custom namespace to each of the most common parts of the service description.

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.Runtime.Serialization;

    [DataContract(Namespace = "http://example.com/faultcontract/datacontract")]
    public class MyFault
    {
    [DataMember]
    public string detail;
    }

    [DataContract(Namespace = "http://example.com/datacontract")]
    public class MyData
    {
    [DataMember]
    public string data;
    }

    [MessageContract(IsWrapped = true, WrapperNamespace = "http://example.com/messagecontract")]
    public class MyMessage
    {
    [MessageHeader(Namespace = "http://example.com/messageheader")]
    public string header;

    [MessageBodyMember(Namespace = "http://example.com/messagebodymember")]
    public string member;
    }

    [ServiceContract(Namespace = "http://example.com/servicecontract")]
    public interface IService
    {
    [FaultContract(typeof(MyFault), Namespace = "http://example.com/faultcontract/operationcontract")]
    [OperationContract]
    void Operation1(MyData data);

    [OperationContract]
    void Operation2(MyMessage message);
    }

    [ServiceBehavior(Namespace="http://example.com/servicebehavior")]
    public class Service : IService
    {
    public void Operation1(MyData data)
    {
    }

    public void Operation2(MyMessage message)
    {
    }
    }

    class Program
    {
    static void Main(string[] args)
    {
    ServiceHost host = new ServiceHost(typeof(Service), new Uri("http://localhost:8000/"));
    Binding binding = new CustomBinding(new TextMessageEncodingBindingElement(), new HttpTransportBindingElement());
    binding.Namespace = "http://example.com/binding";
    host.AddServiceEndpoint(typeof(IService), binding, "");
    host.Description.Behaviors.Add(new ServiceMetadataBehavior());
    host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
    host.Open();
    Console.ReadLine();
    host.Close();
    }
    }

    You can run svcutil against this service to generate all of the WSDL and schema files. Now, we have a way to work backwards from the elements in the WSDL file to find where they are defined in the service description. Running svcutil gives me three WSDL files and seven schema files. One of the schema files covers the standard serialization types that are included with every service. Everything else is customized by my custom namespaces.

    To start the trace through the service description, let's look at example.com.servicebehavior.wsdl.

    <?xml version="1.0" encoding="utf-8"?>
    <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="http://example.com/binding" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="http://example.com/servicebehavior" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" name="Service" targetNamespace="http://example.com/servicebehavior" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:import namespace="http://example.com/binding" location="" />
    <wsdl:types />
    <wsdl:service name="Service">
    <wsdl:port name="CustomBinding_IService" binding="i0:CustomBinding_IService">
    <soap12:address location="http://localhost:8000/" />
    <wsa10:EndpointReference>
    <wsa10:Address>http://localhost:8000/</wsa10:Address>
    </wsa10:EndpointReference>
    </wsdl:port>
    </wsdl:service>
    </wsdl:definitions>

    This gives a reference to the service endpoint itself and links us to the example.com.binding.wsdl file.

    <?xml version="1.0" encoding="utf-8"?>
    <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:i0="http://example.com/servicecontract" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="http://example.com/binding" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" targetNamespace="http://example.com/binding" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsp:Policy wsu:Id="CustomBinding_IService_policy">
    <wsp:ExactlyOne>
    <wsp:All>
    <wsaw:UsingAddressing />
    </wsp:All>
    </wsp:ExactlyOne>
    </wsp:Policy>
    <wsdl:import namespace="http://example.com/servicecontract" location="" />
    <wsdl:types />
    <wsdl:binding name="CustomBinding_IService" type="i0:IService">
    <wsp:PolicyReference URI="#CustomBinding_IService_policy" />
    <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="Operation1">
    <soap12:operation soapAction="http://example.com/servicecontract/IService/Operation1" style="document" />
    <wsdl:input>
    <soap12:body use="literal" />
    </wsdl:input>
    <wsdl:output>
    <soap12:body use="literal" />
    </wsdl:output>
    <wsdl:fault name="MyFaultFault">
    <soap12:fault use="literal" name="MyFaultFault" namespace="" />
    </wsdl:fault>
    </wsdl:operation>
    <wsdl:operation name="Operation2">
    <soap12:operation soapAction="http://example.com/servicecontract/IService/Operation2" style="document" />
    <wsdl:input name="MyMessage">
    <soap12:header message="i0:MyMessage_Headers" part="header" use="literal" />
    <soap12:body use="literal" />
    </wsdl:input>
    <wsdl:output>
    <soap12:body use="literal" />
    </wsdl:output>
    </wsdl:operation>
    </wsdl:binding>
    </wsdl:definitions>

    Now, we have a skeleton for the service operations but are referenced to example.com.servicecontract.wsdl to find descriptions of the messages.

    <?xml version="1.0" encoding="utf-8"?>
    <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:tns="http://example.com/servicecontract" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsa10="http://www.w3.org/2005/08/addressing" targetNamespace="http://example.com/servicecontract" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
    <xsd:schema targetNamespace="http://example.com/servicecontract/Imports">
    <xsd:import namespace="http://example.com/servicecontract" />
    <xsd:import namespace="http://example.com/faultcontract/datacontract" />
    <xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/" />
    <xsd:import namespace="http://example.com/datacontract" />
    <xsd:import namespace="http://example.com/messagecontract" />
    <xsd:import namespace="http://example.com/messageheader" />
    <xsd:import namespace="http://example.com/messagebodymember" />
    </xsd:schema>
    </wsdl:types>
    <wsdl:message name="IService_Operation1_InputMessage">
    <wsdl:part name="parameters" element="tns:Operation1" />
    </wsdl:message>
    <wsdl:message name="IService_Operation1_OutputMessage">
    <wsdl:part name="parameters" element="tns:Operation1Response" />
    </wsdl:message>
    <wsdl:message name="IService_Operation1_MyFaultFault_FaultMessage">
    <wsdl:part xmlns:q1="http://example.com/faultcontract/datacontract" name="detail" element="q1:MyFault" />
    </wsdl:message>
    <wsdl:message name="MyMessage">
    <wsdl:part xmlns:q2="http://example.com/messagecontract" name="parameters" element="q2:MyMessage" />
    </wsdl:message>
    <wsdl:message name="MyMessage_Headers">
    <wsdl:part xmlns:q3="http://example.com/messageheader" name="header" element="q3:header" />
    </wsdl:message>
    <wsdl:message name="IService_Operation2_OutputMessage" />
    <wsdl:portType name="IService">
    <wsdl:operation name="Operation1">
    <wsdl:input wsaw:Action="http://example.com/servicecontract/IService/Operation1" message="tns:IService_Operation1_InputMessage" />
    <wsdl:output wsaw:Action="http://example.com/servicecontract/IService/Operation1Response" message="tns:IService_Operation1_OutputMessage" />
    <wsdl:fault wsaw:Action="http://example.com/servicecontract/IService/Operation1MyFaultFault" name="MyFaultFault" message="tns:IService_Operation1_MyFaultFault_FaultMessage" />
    </wsdl:operation>
    <wsdl:operation name="Operation2">
    <wsdl:input wsaw:Action="http://example.com/servicecontract/IService/Operation2" name="MyMessage" message="tns:MyMessage" />
    <wsdl:output wsaw:Action="http://example.com/servicecontract/IService/Operation2Response" message="tns:IService_Operation2_OutputMessage" />
    </wsdl:operation>
    </wsdl:portType>
    </wsdl:definitions>

    Finally, we have no more WSDL references and these message descriptions link off to each of the individual schema files, which you can go through yourself. For any particular namespace, you can automatically link back to where in the service it was defined. If you've been wondering how to customize a particular namespace in the WSDL or schema, then you can use this example to trace back where the namespace is supposed to go.

    Next time: Increasing the Maximum Fault Size

  • Nicholas Allen's Indigo Blog

    Choosing a Port

    • 3 Comments

    A common question is what port should be chosen for publishing a service. I'm assuming that the question is being asked because no one has told you that you must use a certain port for contractual or operational reasons. If you have to use a particular port, then there isn't a lot of choice in the matter. If you aren't forced to use a particular port, then it becomes more of a game.

    Several protocols have the concept of sharing a port between multiple listeners. If that's the case and you're willing to share, then the default port number for the protocol is typically a good choice. That's port 80 for HTTP, port 443 for HTTPS, or port 808 for Net.Tcp. With port sharing, you only need to be creative in the selection of the path portion of the listener address.

    When you have port sharing but you're not willing to share, you want to do exactly the opposite and avoid the default ports. That minimizes your chances of breaking a whole bunch of applications that use the default port. It only takes one non-sharing application to block everybody else out. The same is true if you have a protocol with no concept of port sharing at all. In either case, you want to pick a port number that is unique enough to not conflict with anyone else on the system but not so unique that it causes you trouble.

    A good rule of thumb is that the port number should be between 5000 and 10000. The lower numbered ports tend to have lots of people already claiming that space. The number 5000 is a historical relic from the DCOM days as a recommendation; it doesn't actually have a special significance for web services. The higher numbered ports tend to start straying into the range of randomly generated client ports. This isn't a problem if the service is already running because the random port generator won't pick that port number. However, it is a problem if the client port is picked first and then your service tries to start because the port will be temporarily allocated. The solution is not quite as simple as just avoiding the client ports that Windows uses because proxy servers and other intermediaries might be forced to use an equivalent port but be using a different client port allocation algorithm.

    Next time: Custom Namespaces

  • Nicholas Allen's Indigo Blog

    Jiggling the Handle

    • 0 Comments

    Minor technical hangup Wednesday when the normal post ended up not appearing in the RSS feed. I fixed it a few hours later, but if you're still missing Revealing the Hierarchy, then you can get it through the web link. Trackbacks and comments for the post disappeared in the process. Sorry.

    The repair work also ended up knocking the Thursday post out of the queue unintentionally. Everything should be back to normal tomorrow.

  • Nicholas Allen's Indigo Blog

    Revealing the Hierarchy

    • 0 Comments

    I have a hierarchy of types that I'm using in service data contracts. When I change the service implementation, sometimes this shifts things so that a different set of the base classes and derived classes are exposed by the service. How do I present a consistent set of types without losing the ability to change the service implementation?

    The issue here is that two data contracts that are essentially the same can differ in detail depending on whether the projected type is the base class of a hierarchy or the most derived class of a hierarchy. One approach is to use a disciplined consistency in the construction of the service contract so that the set of exposed types does not change over time. Often, this requires some coordination as everyone working on each of the contracts has to work in a coordinated fashion. A mistake in the design will cause the set of types to change, but this can then be tested for by automated processes that compare the set of projected types against a baseline.

    The alternative approach is essentially the opposite, which is to project all of the types in the hierarchy regardless of whether they're used in the current implementation. This approach again keeps the set of types consistent as the service implementation changes even though the set of types is different than the one in the first approach. You can project the additional types using standard mechanisms, such as KnownType declarations.

    The two approaches have different consequences for service versioning. In the first approach, maintenance of the set of types is very simple but there is a coupling present in the service contract that is very much in the spirit of compatibility requirements for binary linking. In the second approach, maintenance of the set of types tends to be more unruly but the degree of polymorphism offered by having a full type hierarchy is something that can be leveraged in future versions. By adding new types that superset the hierarchy, additional information can be passed for service calls that are aware of the new version. The older type bindings are still usable although obviously they don't have any access to the new features.

    Next time: Choosing a Port

Page 1 of 1 (23 items)