Chapter 21. Guidance

<feature><title>In This Chapter</title> </feature>

Introduction

This chapter provides a list of things to do and things to avoid doing in building solutions with the Windows Communication Foundation. The list has been assembled over the course of nearly two years of working with the technology, and assisting early adopters to use it.

Adopting the Windows Communication Foundation

Adopting the Windows Communication Foundation

DO NOT plan to rebuild existing applications so as to use the Windows Communication Foundation.

Planning to rebuild existing applications to make use of a new technology fails to take into account that there are always new technologies emerging and new ideas about how to build software. It yields a view of the future of one’s software with artificial milestones of accomplishment in migrating from one new technology to the next. It postpones the delivery of new functionality while existing functionality is re-engineered.

DO develop a detailed vision of how one’s software should ideally be constructed that takes into account the capabilities of new technologies as well as current design principles.

DO build exemplars based on that vision.

Given a vision of how one would like one’s software to work, one proceeds to build exemplars. An exemplar is a solution to a small set of new functional requirements that is constructed in accordance with the tenets of the vision of how one’s software should be constructed. Thus, in building an exemplar, one aims to develop a solution to a small new set of requirements, rather than merely reproduce a solution to an existing set of requirements using new technologies and ideas. Further, whereas a pilot is typically something that is hastily constructed to confirm some possibility and then discarded, an exemplar is built very carefully as an example that one would like all of the software developers in one’s organization to follow, using pieces that one would like them to be able to re-use. Therefore, in building an exemplar, one also builds or extends a reusable framework, and the utility of that framework will be proven through the construction of the exemplar.

The framework will typically hide the native interfaces of new vendor technologies away behind interfaces of one’s own design, interfaces that reduce the surface area of the vendors’ technologies to the set of options that one’s developers actually need. So, by virtue of such a framework, one’s developers would not use the Windows Communication Foundation directly, but only the pertinent elements thereof surfaced through the framework. Having such a framework accelerates the productivity of one’s developers, saving them from having to learn all about a new technology, and it constrains them to follow techniques that are in accordance with one’s vision, and proven through the construction of the exemplar.

DO consider how to integrate one’s existing software with new software built using the reusable framework developed in the construction of the exemplar.

Asking how to integrate one’s existing software with a proven, reusable framework developed precisely in accordance with one’s own requirements and one’s own vision of how one’s software should be constructed is entirely different from asking how to migrate one’s existing software to use the Windows Communication Foundation. First, it is a question that one generally finds oneself better equipped to answer, because it is not a question of moving to a vast and unfamiliar new technology, but rather a question of integrating with a framework that is well-understood because one built and used it oneself. Second, it does not presuppose discarding existing technologies, but merely allows one’s existing software to work properly with one’s new software. Third, it is a question about how to progress with the delivery of new functionality, rather than a question about how to re-engineer the delivery of existing functionality.

DO delegate the maintenance of one’s vision, the construction of new exemplars, and the concomitant evolution of one’s framework to one team of software developers.

DO delegate the work of using the framework to deliver solutions over to the majority of one’s software developers.

Of course, one’s vision of how one’s software should ideally be constructed will continue to evolve, and it should do so as new technologies and new design principles emerge. One should delegate the work of maintaining that vision and one’s framework for implementing it over to one team of software developers that is tasked with absorbing new technologies and ideas and progressively incorporating them into the framework through the construction of new exemplars. The majority of the software developers should have the task of using the existing framework to deliver new functionality to users. This way of organizing one’s development team is used by several successful software vendors.

A different organizational pattern is to allow one group of developers the leeway of using new technologies that emerge, but constraining other groups to using an existing framework, while charging both groups with delivering new functionality to end users. That organization does not provide for the systematic absorption of new technologies and ideas, and leads to the stagnation of the framework. It also leads to resentment of the group that gets to play with the new technologies. The pattern recommended here, where one group maintains the framework, absorbing new technologies, while everyone else delivers solutions for end users, does not necessarily lead to the same resentment. Some developers enjoy working on frameworks, whereas others enjoy delivering finished products, and provided they are assigned to do what they enjoy, they can be happy in their work.

Yet another organizational pattern delegates the maintenance of the framework to one group of developers, and the delivery of end user solutions to everyone else, but tasks the latter group with the absorption of new technologies. When a new technology is found to work for a particular type of solution, the maintainers of the framework extend the framework so that it can be used to reproduce that solution. This organizational pattern is typically used to reduce the cost of maintaining the framework by having it done by a team of capable developers that are less expensive because they work in a third-world country with a lower cost of living. Developers close to the high-paying customers in the first-world countries use whatever new technologies and design principles are available to them to give those customers what they want as quickly as possible. This organizational pattern can work, provided the groups working on new customer solutions are coordinated with one another so that they do not reproduce one another’s efforts.

Working with Windows Communication Foundation Addresses

DO specify base addresses for services that are not hosted within IIS.

DO specify base addresses for all the transport protocols that one might want the service to support.

DO specify endpoint addresses that are relative to the base addresses.

Working with Windows Communication Foundation Addresses

DO NOT use absolute addresses for endpoints.

Compare this undesirable configuration that uses an absolute address for an endpoint

<!-- This is an example of what not to do: -->
<service
        name="DerivativesCalculator.DerivativesCalculatorServiceType"
        <endpoint
                address="http://localhost:8000/Derivatives/Calculator"
                binding="wsHttpBinding"
                contract="DerivativesCalculator.IDerivativesCalculator" />
</service>

with this desirable configuration that uses an address for an endpoint that is relative to a base address:

<service
        name="DerivativesCalculator.DerivativesCalculatorServiceType"
        <host>
                <baseAddresses>
                  <add baseAddress="http://localhost:8000/Derivatives/" />
                  <add baseAddress="net.tcp://localhost:8010/Derivatives/" />
                </baseAddresses>
        </host>
        <endpoint
                address="Calculator"
                binding="wsHttpBinding"
                contract="DerivativesCalculator.IDerivativesCalculator" />
</service>

The latter, desirable configuration has two virtues.

First, if one changes the binding to one that uses a different transport protocol, one simply has to alter the name of the binding specified in the configuration, with no change to the endpoint address being required:

<service
        name="DerivativesCalculator.DerivativesCalculatorServiceType"
        <host>
                <baseAddresses>
                  <add baseAddress="http://localhost:8000/Derivatives/" />
                  <add baseAddress="net.tcp://localhost:8010/Derivatives/" />
                </baseAddresses>
        </host>
        <endpoint
                address="Calculator"
                binding="netTcpBinding"
                contract="DerivativesCalculator.IDerivativesCalculator" />
</service>

In the former, undesirable configuration, two changes would have been required to achieve the same objective: a change to the name of the binding and a change to the scheme of the endpoint address:

<service
        name="DerivativesCalculator.DerivativesCalculatorServiceType"
        <endpoint
                address="net.tcp://localhost:80o0/Derivatives/Calculator"
                binding="nettcpBinding"
                contract="DerivativesCalculator.IDerivativesCalculator" />
</service>

The second virtue of the recommended configuration is that directing a browser to the specified HTTP base address will yield a page confirming the availability of the service. Being able to do that can be very useful for debugging.

Working with Windows Communication Foundation Bindings

DO use the predefined BasicHttpBinding to interoperate with software that was not developed using Microsoft .NET, or that was developed using .NET web services.

DO anticipate having to expend considerable effort getting the predefined WSHttpBinding and WS-FederationBinding to work with software that was not developed using Microsoft .NET and that uses implementations of one or more WS-* protocols for communication.

The predefined BasicHttpBinding implements a profile—the WS-I Basic Profile 1.1, which specifies how a small set of communications protocols can be used to maximize the likelihood of interoperability. WSHttpBinding and WSFederationBinding incorporate implementations of a number of communication protocols for which profiles do not yet exist, including WS-Security, WS-ReliableMessaging, WS-SecurityPolicy, WS-AtomicTransaction, WS-Coordination, and WS-Trust. Those bindings could be used as a basis for communication using those protocols with other implementations of the same protocols, but accomplishing that might require considerable effort. Anticipate that as profiles for those protocols are developed, versions of the bindings with the appropriate defaults will be made available, and then interoperability with other software that conforms to the profiles should be easy to achieve.

DO analyze the solution to identify services with contended resources.

DO use the predefined NetMsmqBinding to manage access to that service’s resources.

If there are any services with resources to which access will be contended by clients, it is hard to imagine any reason sufficient to justify not having requests for those resources come via a queue. Using a queue for access to the service and its contended resources will ensure that the servicing of one client’s request will not interfere with the servicing of another’s—by causing a deadlock, for example—because each client’s requests will be dealt with in sequence. More important, the developers of the service’s clients will know to accommodate their requests for access to the service’s resources being dealt with as the resources become available rather than right away.

The easiest way to have requests to a service go via a queue is to have them sent via the Windows Communication Foundation’s predefined NetMsmqBinding. There are two limitations to be aware of in using that binding, though. First, messages conveyed via the NetMsmqBinding pass through an MSMQ queue, and MSMQ can only accommodate messages up to 4MB in size. Second, Windows Communication Foundation services that receive messages via the NetMsmqBinding must be deployed on the same computer system as the MSMQ queue from which the incoming messages are read.

DO use the predefined NetMsmqBinding to guarantee message delivery.

DO use the predefined NetTcpBinding for communication between applications built using the Windows Communication Foundation that are deployed on different computer systems, except if one of the preceding guidelines about bindings applies.

The predefined NetTcpBinding offers a great many configuration options by which it can be adapted to different scenarios. More important, it can accommodate duplex communication, by which a client can send a request to a service, and the service can respond asynchronously.

DO, however, use an HTTP binding for scenarios where scaling out over load-balanced instances of services is required.

The following advice, written about how to load-balance .NET Remoting servers using Network Load Balancing, applies to Windows Communication Foundation servers as well:

You cannot load balance across a serve farm [when using TCP], due to the machine affinity of the underlying TCP connection. This severely limits your application’s ability to scale out. To provide an architecture that can scale out, use IIS as the host, combined with [communication via HTTP]. This configuration provides the greatest scale out ability, because each method call over [HTTP] only lives for the life of the method call and maintains no machine affinity (Meier, Vasireddy, Babbar, and Mackman 2004, 497).

DO use a custom HTTP binding rather than a predefined HTTP binding for scenarios where scaling out over load-balanced instances of services is required.

The predefined HTTP bindings include a connection HTTP header in messages with the value Keep-Alive. That enables clients to create persistent, reusable connections to services that support doing so. That behavior can create a machine affinity that can interfere with load-balancing. The System.ServiceModel.Channels.HttpTransportBindingElement has a KeepAliveEnabled property that can be assigned a value of false to disable support for persistent connections. So, for load-balancing, use a custom binding that incorporates an instance of System.ServiceModel.Channels.HttpTransportBindingElement with the value of its KeepAliveEnabled property set to false.

DO use the predefined NetNamedPipeBinding for communication between applications built using the Windows Communication Foundation that will execute within different processes on the same computer system, except if one of the preceding guidelines about bindings applies.

The predefined NetNamedPipeBinding offers all the benefits that the NetTcpBinding does. However, it yields better throughput between applications in different processes on the same computer system than the NetTcpBinding does. Switching to the NetTcpBinding if the applications are redeployed onto separate computer systems should entail a change only to the choice of binding in configurations of the applications.

Working with Windows Communication Foundation Bindings

DO NOT use the Windows Communication Foundation in the .NET Framework 3.0 for communication between different application domains in the same process.

The .NET Common Language Runtime uses .NET Remoting to allow objects in one application domain access to objects in another application domain in the same process (Box and Sells 2003, 274). The version of the Windows Communication Foundation in the .NET Framework 3.0 is not optimized for communication between different application domains in the same process.

DO use the predefined NetPeerTcpBinding for communication when no Domain Name Service (DNS) is available.

DO provide a custom peer resolver service, if necessary, to help ensure messages reach their destinations.

On Windows XP SP2 and Windows Vista, the predefined NetPeerTcpBinding relies on the implementation of the Peer Name Resolution Protocol in Windows Peer-to-Peer Networking as a substitute for DNS. That protocol relies on peers running a consumer operating system to do name resolution, as opposed to relying on, say, Internet root name servers maintained by the Internet Network Information Center and agencies of the United States military. Therefore, although it is perfectly suitable for casual endeavors such as Internet gaming, it is unsuitable for critical communications. A more certain degree of reliability can be accomplished using a custom peer resolver service, at an address known to all the peers, that will do the peer name resolution.

Working with Windows Communication Foundation Bindings

AVOID using the WSDualHttpBinding.

The WSDualHttpBinding provides for duplex communication over HTTP. As explained already, duplex communication involves clients sending requests to a service, and the service responding asynchronously. That form of communication will not usually work over HTTP when the client is behind a firewall because firewalls are customarily configured to block inbound HTTP connections. If one has the flexibility to modify the configuration of the firewall, it is preferable to open a port for use by the NetTcpBinding because doing so will yield better performance with at least as much flexibility. If the firewall cannot be reconfigured, the clients will have to poll the service for responses to their requests. Clients used by people from within their homes will typically be behind some network address translator and will not have a routable Internet Protocol address to which the server’s responses can be delivered. They too will have to poll the service for responses to their requests. If the clients and the service are all deployed on the same local area network with no firewalls or network address translators, the WSDualHttpBinding would work properly, but the NetTcpBinding would yield better performance.

Working with Windows Communication Foundation Bindings

DO NOT activate unnecessary binding features.

DO deactivate unnecessary binding features.

Securing the exchange of messages reduces message throughput, so do not secure the exchange of messages unless doing so is warranted by one’s threat model. Reliable sessions also reduce message throughput, especially over HTTP, so do not use that binding option either unless it satisfies an explicit nonfunctional requirement.

One must not only avoid using unnecessary binding features, but also take care to deactivate unnecessary ones that are on by default to maximize security and robustness. For instance, comparing the performance of the Windows Communication Foundation’s predefined NetTcpBinding with its default settings to the performance of ASP.NET web services is meaningless. The former is a secure, reliable, duplex communication channel, whereas the latter is an unsecured, request-response channel with no reliability assurances. However, the NetTcpBinding can readily be configured to outperform ASP.NET web services.

DO, in constructing custom binding elements, use custom operation selector and message inspector behaviors to facilitate debugging.

Adding a custom operation selector and a custom message inspector behavior to the endpoint dispatcher for a service can be very helpful in debugging custom binding elements. The Windows Communication Foundation will pass messages received from the Channel Layer through a custom operation selector and a custom message inspector before attempting to deserialize the messages and invoke the methods of a service. So, those custom behaviors are useful for confirming that the messages are in fact passing through the custom channel, and for examining the messages themselves to diagnose any errors that the channel might have introduced. How to add custom operation selectors and custom message inspectors is covered in Chapter 13, “Custom Behaviors.”

Working with Windows Communication Foundation Contracts

DO design contracts using scenarios.

A question that is often asked but seldom properly answered is how to know which services to build. Given a set of requirements, for instance, how is one to identify the services needed to implement the solution?

One view is that attempts “to take a set of business requirements and from them derive a technology model” are often doomed because in “failing to work closely enough with the business, there is often a large disconnect between the business and the IT solution provided” (Sehmi and Schwegler 2006, 34). Proponents of this view argue that the analysis should begin with a business model that identifies the capabilities of the organization as well as its processes and service level expectations (Sehmi and Schwegler 2006, 34–35). The next step is to define a service model that identifies, in particular, “[e]xternally consumable service interfaces” mapped to the business capabilities (Sehmi and Schwegler 2006, 35–36). In arguing for this approach, which is a reasonable one, Arvindra Sehmi and Beat Schwegler pose this key question: “Given a business model, [...] how can you translate that [...] into a service model that you can ultimately implement?” (Sehmi and Schwegler 2006, 40). The answer they offer is this:

To identify and document the [...] items in a service model, you do not need to use radically new analysis techniques. Rather, you can use existing skills such as conventional object-oriented analysis and design skills (Sehmi and Schwegler 2006, 40).

This answer is very likely mistaken, which is most unfortunate because following this advice will likely still yield a poorly designed service model after the considerable expense of analyzing the business model. Why is the answer mistaken? Well, compare it with the insightful guidance formulated by Krzysztof Cwalina and Brad Abrams based on their experience in developing the .NET Framework:

DO NOT rely on standard design methodologies when designing the public APIs layer of a framework. Standard design methodologies (including object-oriented design methodologies) are optimized for the maintainability of the resulting implementation, not for the usability of the resulting APIs. Scenario-driven design together with prototyping, usability studies, and some amount of iteration is a much better approach (Cwalina and Abrams 2006, 16).

Cwalina and Abrams’ remarkable book, Framework Design Guidelines: Conventions, Idioms, and Patterns for Resuable .NET Libraries, while reflecting on what they learned building a class library framework rather than a set of services, still offers wonderful guidance for designers of services. For after all, a set of services does indeed constitute, in their words, a “public API[] layer” and their book provides a rare distillation of the wisdom accrued by a large number of people engaged in the development of a modern and extremely popular programming framework. The very fine level of detail in their work provides abundant evidence that the authors have had considerable experience successfully solving very concrete engineering problems. More important, the fact that they frequently indicate and analyze shortcomings in the design of the .NET Framework indicates that much of the wisdom they offer is of the most precious kind: the kind that is acquired by learning from mistakes.

The most important lesson that they offer is that design of programming interfaces should follow the “Principle of Scenario-Driven Design” (Cwalina and Abrams 2006, 13). They write:

To optimize the overall productivity of the developers using a framework, [...] framework design should be focused around a set of common scenarios to the point where the whole design process is scenario-driven. We recommend that framework designers first write code that the users of the framework will have to write in the main scenarios, and then design the object model to support these code samples. [...] Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios (Cwalina and Abrams 2006, 13).

They note that this approach “is similar to processes based on test-driven development (TDD) or on use cases,” but correctly point out that “TDD is more heavyweight as it has other objectives beyond driving the design of APIs. Users are describing scenarios on a higher level than individual API calls” (Cwalina and Abrams 2006, 13).

The steps that they propose, in articulating this approach, are the ones to follow in designing service contracts:

  1. “[S]tart with producing a scenario-driven API specification” containing a listing of “the top five to ten scenarios for a given technology area and show code samples that implement these scenarios [...] in at least two programming languages” (Cwalina and Abrams 2006, 14).

  2. “[D]esign APIs by first writing code samples for the main scenarios and then defining the object model to support the code samples (Cwalina and Abrams 2006, 15).

  3. “[O]rganize usability studies to test APIs in main scenarios” (Cwalina and Abrams 2006, 19).

This guidance, which focuses the effort in designing an interface primarily toward its perspicacity and usability, is actually especially relevant to the task of designing service contracts. Developers writing clients for services very often have to figure out how to use the services from the metadata alone, without the luxury of extensive installed documentation and samples. Designers of services must therefore do their utmost not only to simplify the interfaces, but also to ensure that how they are meant to be used is conveyed through the design of the interface itself—that the interface must be self-documenting. Here are some specific guidelines offered by Cwalina and Abrams for accomplishing those objectives:

  • “It has to be easy to identify the right set of types and members for common programming tasks” (Cwalina and Abrams 2006, 20).

  • Provide convenience overloads that require setting fewer parameter values (Cwalina and Abrams 2006, 21).

  • Ensure that the default values are the right ones (Cwalina and Abrams 2006, 21).

  • “Think of the object model as a map—you have to put clear signs about how to get from one place to another. You want a property to clearly point people to what it does, what values it takes, and what will happen if you set it” (Anderson 2006, 21).

  • “[E]nsure that the main [...] namespace contains only types that are used in the most common scenarios. Types used in advanced scenarios should be placed in subnamespaces” (Cwalina and Abrams 2006, 21).

  • “Do not have members intended for advanced scenarios on types intended for mainline scenarios” (Cwalina and Abrams 2006, 1, emphasis omitted).

  • “[E]xceptions should clearly describe their cause and the way the developer should modify their code to get rid of the problem” (Cwalina and Abrams 2006, 23).

  • “Do not be afraid to use verbose identifier names. Most identifier names should clearly state what each method does and what each type and parameter expects” (Cwalina and Abrams 2006, 26, emphasis omitted).

  • “[I]nvolve user education experts early in the design process. They can be a great resource for spotting designs with bad name choices and designs that would be difficult to explain” (Cwalina and Abrams 2006, 26).

  • Reserve “the best type names for the most commonly used types” (Cwalina and Abrams 2006, 26).

DO provide explicit namespaces and names for service contracts, operation contracts, data contracts, and data members.

One might not like the defaults if one knew what they were. More importantly, if the defaults were to change in subsequent releases of the technology and a service was upgraded to use that new release but its clients were not, the clients’ messages might no longer be formatted in the way that the service expects them to be formatted.

Working with Structural Contracts

DO use data contracts rather than serializable types wherever possible.

The System.Runtime.Serialization.DataContractSerializer used for serializing and deserializing data contracts outperforms the System.Xml.Serialization.XmlSerializer used for serializing and deserializing serializable types. That performance difference is important because the serialization of data items into messages and the deserialization of messages into data items tend to be the biggest bottlenecks in service-oriented programming solutions. Furthermore, data contracts are explicit about what gets serialized, whereas it is solely the public fields of serializable types that are implicitly serializable.

Working with Structural Contracts

DO NOT, in defining data contracts, use any types except .NET value types as data members, unless the types are themselves data contracts composed of hierarchies of .NET value types.

.NET value types map to XML Schema data types, which, in turn, represent types that are commonly found in type systems. Therefore, data contracts that are nothing more than hierarchies of .NET value types can deserialized into meaningful data structures on other platforms. Types that .NET developers often want to use in defining data members, but that they should not use, are System.Data.DataSet, System.Collections.Hashtable, and System.Collections.Generic.Dictionary<TKey,TValue>. As explained in Chapter 2, “The Fundamentals,” service-oriented programming aims at developing software entities that are loosely coupled to one another by their shared knowledge of explicitly defined message formats, rather than tightly coupled by their shared knowledge of the same types. Adding a data member that is a .NET reference type like System.Data.DataSet, System.Collections.Hashtable, or System.Collections.Generic.Dictionary<TKey,TValue> to a data contract defeats the purpose of using a data contract to define a message format because only software entities that are tightly coupled to one another by their shared knowledge of the same types could exchange the messages. If the ease of using types like System.Data.DataSet, System.Collections.Hashtable, or System.Collections.Generic.Dictionary<TKey,TValue> seems more important than the objective of creating loosely coupled software entities, one might want to reconsider using a service-oriented programming technology at all.

DO use instance fields for data members regardless of whether they are public or private.

DO have the data members, and any methods included in data contract classes, in separate partial types.

These recommendations are for enhancing the readability of data contracts. In evaluating them, consider the definition of a data contract in Listing 21.1.

The first of the two recommendations contravenes the familiar design guideline “that you should almost never use publicly exposed instance fields, but use properties instead” (Cwalina and Abrams 2006, 35). However, in working with data contracts, one would often like to know what members the data contract includes, and that is more easily ascertained by glancing at a list of instance fields than by scrolling through the code for a number of properties. That readability is further enhanced when the data member instance fields are all defined in a separate partial type, as they are in Listing 21.1, uncluttered by any of the data contract class’s behavior.

Example 21.1. Data Contract Definition

//MyDataContract.cs
[DataContract(Name="...",Namespace="...")]
public partial class MyDataContract
{
        [DataMember(Name="...")]
        public XType MyField;
}

//MyDataContractBehavior.cs
public partial class MyDataContract
{
    public MyDataContract()
    {
    }

    public MyDataContract(XType myField)
    {
        this.MyField = myField;
    }

    public MyDataContract(MyBusinessType myBusinessType)
    {
        [...]
    }

    public MyBusinessType CreateBusinessType()
    {
        [...]
    }
}

DO NOT include any business logic in the definition of data contract classes.

Data contracts are meant for defining message formats, not business objects. Therefore, besides data members, data contracts should have only constructors, including constructors for creating instances from business objects, and methods for creating instances of business objects from instances of the data contracts.

Data Contract Definition

AVOID using message contracts.

Message contracts are for differentiating the data that should be among the headers of a message from the data that should be in the body. However, it has become customary to use headers only for information pertaining to communication protocols, rather than to use them for conveying substantive information. Therefore, unless one is defining a communication protocol or using one that defines message headers, one should have no use for message contracts.

Data Contract Definition

DO NOT have more than one message body member in a message contract.

If one is justified in using a message contract, that message contract should have just one message body member that is a data contract.

DO always have data contracts implement System.Runtime.Serialization.IExtensibleDataObject.

Implementing that interface ensures that the data contract will be able to be used together with newer versions of the same contract that include optional members that the original version of the contract did not include. By implementing the System.Runtime.Serialization.IExtensibleDataObject interface, the data contract provides memory that the Windows Communication Foundation can use for storing additional data members defined in later versions of the same data contract. Thus, a newer version of the data contract with additional operational data members can be deserialized into an older version and when that older version is serialized again, none of the data for the additional data members of the newer version will be lost.

Working with Behavioral Contracts

DO add appropriate fault contracts to each operation contract.

DO include a fault contract defining a fault signifying that the endpoint has been retired on each operation contract.

Adding fault contracts to operation contracts serves to incorporate, into the metadata for a service, an indication of the faults that might occur in each of the operations. That allows developers writing code that invokes those operations to anticipate what could go wrong and to write their code accordingly.

One fault that could always occur would be due to the provider of the service retiring the endpoint that included an operation that the client has invoked. In anticipation of that, one should add to every operation contract a fault contract signifying the retirement of the endpoint. That would signal the possibility of the endpoint being retired to developers writing code to use the operations, and allow them to plan for it. When the endpoint is retired, one would modify the methods implementing the operations exposed at that endpoint to do nothing more than throw the fault exceptions indicating that the endpoint is no longer in use. The information included with those faults could indicate where the metadata for a replacement endpoint might be found.

DO include a default operation in every service contract.

A default operation is defined by assigning the value "*" to the Action parameter of the OperationContract attribute. Here is an example of a default operation:

[OperationContract(Action="*")]
Message Default(Message input);

Any incoming message not addressed to one of the other operations of a contract will be directed to the default operation. A default operation is more useful if it takes a System.ServiceModel.Channels.Message object as its input parameter, as in the example above, for that enables the operation to accept incoming data in any format. The virtue of including a default operation in a contract is that when a client is attempting to connect, and the binding is configured properly but the format of the message is mistaken, the messages will at least be delivered to the default operation. This thereby confirms that the binding is correct and that the remaining defects are restricted to a mismatch in contract definitions.

DO make service contracts duplex by default.

Duplex service contracts define one-way operations by which clients send requests to services, and one-way operations by which services can send to clients any number of responses to each request. Here is an example of one:

[ServiceContract(
        SessionMode=SessionMode.Required,
        CallbackContract=typeof(ICalculatorClient))]
public interface ICalculatorDuplex
{
    [OperationContract(IsOneWay = true)]
    void CalculateDerivative(
                        string requestIdentifier,
                        string[] symbols,
            decimal[] parameters,
            string[] functions);
}

public interface ICalculatorClient
{
    [OperationContract(IsOneWay = true)]
    void SendCalculationResult(
                string requestIdentifier,
                double result);
}

The asynchronous exchange of messages provided by duplex contracts gives one more flexibility in designing how services process requests. One reason is that the contract itself implies that no client thread will be blocked by a pending request. Another reason is that the service can apportion its response to the client, providing the most urgent data first, and the remainder in subsequent portions.

Duplex contracts are not supported by all bindings. Among the bindings that do support them are the NetTcpBinding and the NetNamedPipesBinding, and their support for duplex contracts is one of the reasons those bindings are recommended.

Working with Behavioral Contracts

DO NOT overload the names of the methods by which operation contracts are defined.

If the methods by which two operation contracts are defined have the same name, there must be different names assigned to the Name properties of the operation contracts:

[OperationContract(Name="FindUserByName")]
User FindUser(string username);
[OperationContract(Name="FindUserByIdentifier")]
User FindUser(Guid userIdentifier);

Working with Windows Communication Foundation Services

DO reduce latency and maximize throughput by having all client requests to a service go to a single, multithreaded instance of one’s service type.

Left to its own devices, the Windows Communication Foundation will create a new instance of a service type to process each client request. It does so in order for Windows Communication Foundation applications to be as robust as possible by default, with each request being handled by a thread-safe instance of the service type. To reduce latency and optimize throughput, it would be better for just a single instance of the service type to be created to handle all client requests, and for that instance to be multithreaded.

To specify that all client requests are to be directed to the same instance of a service type, assign a value of System.ServiceModel.InstanceContextMode.Single to the InstanceContextMode parameter of the service type’s System.ServiceModel.ServiceContract attribute. To specify that the service type be multithreaded, assign a value of System.ServiceModel.ConcurrencyMode.Multiple to the ConcurrencyMode parameter of the attribute. Here is an example:

[ServiceContract(
        InstanceContextMode=InstanceContextMode.Single,
        ConcurrencyMode=ConcurrencyMode.Multiple)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator

DO use transactional types to synchronize access to shared data in multithreaded service types.

In a brilliant contribution to MSDN Magazine, Juval Lowy documented some conceptually very simple types that he designed using the .NET Framework 2.0’s System.Transaction namespace that allow one to apply the transactional programming model to the problem of writing thread-safe code (Lowy 2005). Transactional programming is very familiar to software developers, easy to do, and implemented in very much the same way on different development platforms. By contrast, techniques for synchronizing the access of multiple threads to shared resources are less well known, less reliable for avoiding trouble, and vary more considerably from one development platform to another. Because of the negligible overhead of the Lightweight Transaction Manager provided by the System.Transaction namespace, Lowy was able to design types, shared access to which can be efficiently managed using transactions. Those types offer a more reliable way of avoiding thread synchronization problems, which are often very difficult to diagnose and fix. In addition, the technique for using those types will be familiar to any developer who has ever performed the very common task of programming a transaction.

DO make the option of hosting services within IIS one’s first choice.

IIS is engineered for scalability, reliability, and fault tolerance, and its security has been tested and reinforced. One good reason for not using IIS to host one’s services is that on operating systems prior to Windows Vista, IIS can only host services that communicate over HTTP, and one’s choice of communication protocols should take precedence over one’s choice of host. A second reason for not using IIS to host one’s service is if one is a software vendor seeking to simplify deployments onto customers’ systems. Not having to deploy one’s solution into IIS is easier, and that is important when the deployment has to be repeated for every customer. A third good reason for not using IIS is that IIS can recycle a process at any time, making it unsuitable for hosting services that maintain state in a nondurable store. Writing services that maintain state in a durable store will be made easier by the durable services feature that is to be included in the next release of the Windows Communication Foundation.

DO explicitly call the Close() method of System.ServiceModel.ServiceHost when hosting a service within a .NET application.

Explicitly calling the Close() method of System.ServiceModel.ServiceHost when hosting a service within a .NET application, as in this code snippet, serves to terminate the service more quickly, which can expedite shutting down the .NET application:

using (ServiceHost host = new ServiceHost(
    serviceType)}
    ))
{
    try
    {
        host.Open(new TimeSpan(0,0,30));

        Console.WriteLine(
            "The service is available."
        );
        Console.ReadKey(true);

        host.Close(new TimeSpan(0,0,30));

    }
    catch(TimeoutException timeoutException)
    {
        ...
    }
}

DO also specify explicit timeouts in calls to the Open() and Close() methods of System.ServiceModel.ServiceHost.

Otherwise, if the transition to the Open or Closed state waits on some long-running process, the calls to Open() and Close() might not complete in any definite amount of time.

DO build service types into class libraries.

Building service types into class libraries makes it easy to redeploy them into different kinds of hosts. They can be hosted within .NET applications by having the .NET applications reference their class libraries. They can be hosted within IIS by copying them into the bin subdirectory of an IIS virtual directory along with a Web.config file with their configuration information.

Working with Windows Communication Foundation Services

AVOID having to rely on session state information.

DO incorporate smaller volumes of session state information into messages.

DO use a database to store larger volumes of state information.

DO use the Extension property of the System.ServiceModel.InstanceContext object to maintain smaller volumes of session state information when maintaining it outside of messages proves unavoidable.

DO use the Extension property of the System.ServiceModel.ServiceHost object to store smaller volumes of application state information.

Listing 21.2 provides an example of using the Extension property of the System.ServiceModel.InstanceContext object to store session state information. Listing 21.3 provides an example of using the Extension property of the System.ServiceModel.ServiceHost object to maintain application state information.

Example 21.2. Using InstanceContext.Extension

public class MyExtension: IExtension<InstanceContext>
{
        public MyDataType MyData = null;
}

public void Initialize(MyDataType myData)
{
        MyExtension extension = new MyExtension();
        extension.MyDataType = myData;
        OperationContext.InstanceContext.Extensions.Add(myData);
}
public MyDataType Use()
{
        MyExtension extension =
           OperationContext.InstanceContext.Extensions.Find<MyExtension>();
        return extension.MyData;
}

Example 21.3. Using ServiceHost.Extension

public class MyExtension: IExtension<InstanceContext>
{
        public MyDataType MyData = null;
}

public class Host
{
    public static void Main(string[] args)
    {
        Type serviceType = typeof(DerivativesCalculatorServiceType);

        using(ServiceHost host = new ServiceHost(
            serviceType
            ))
        {
                        MyExtension extension = new MyExtension();
                        extension.MyDataType = myData;
                        host.Extensions.Add(extension);

            host.Open();

            Console.WriteLine(
                "The derivatives calculator service is available."
            );
            Console.ReadKey(true);

            host.Close();
         }
     }
}

public class DerivativesCalculatorServiceType: IDerivativesCalculator
{
    decimal IDerivativesCalculator.CalculateDerivative(
        string[] symbols,
        decimal[] parameters,
        string[] functions)
    {

        MyExtension extension =
                   OperationContext.InstanceContext.Extensions.Find<MyExtension>();
                MyDataType myData = extension.MyData;
                [...]
    }
}

DO consider using the Windows Communication Foundation’s ASP.NET Compatibility Mode to leverage the rich session management options of ASP.NET when network load-balanced service instances have to maintain session state.

DO use a type derived from System.ServiceModel.ServiceAuthorizationManager for authorization rather than System.Security.Permissions.PrincipalPermission attributes.

System.Security.Permissions.PrincipalPermission must be embedded in the code of a service type. The System.ServiceModel.ServiceAuthorizationManager type that is to be used to control authorization can be identified in the configuration of the service, and can be in any assembly that the .NET Common Language Runtime loader can locate when the service type is constructed.

DO consider using Windows Workflow Foundation rules to program types derived from System.ServiceModel.ServiceAuthorizationManager to control authorization.

Expressing authorization criteria in the form of Windows Workflow Foundation rules offers considerable flexibility. Windows Workflow Foundation rules can be defined either in code or in XML, and editors are provided that can be used for creating, examining, and editing rules.

Ensuring Manageability

DO have every service type implement a service contract for obtaining information to assist in the administration of the service, and by which administrators can control the service.

The Windows Communication Foundation provides a number of data and control points for service administrators in the form of performance counters, activity tracing, message logging, and the WMI provider. However, those data and control points are designed for the administration of generic services, and naturally cannot take into account the specific requirements of every actual service. Therefore, one should consider designing service contracts with operations for exposing data and control points that do take the specific administration requirements of one’s services into account, and those service contracts should be implemented by those services.

DO provide a comprehensive health model for one’s service.

DO document the proper configuration for the service in the health model, possibly with reference to the use of the Windows Communication Foundation’s Service Configuration Editor.

DO activate the built-in performance counters and make use of those in developing the health model.

DO activate only the performance counters at the service level initially, and use the performance counters at the endpoint and operation level solely for diagnosing actual defects.

DO activate the WMI provider and make use of the information it exposes in developing the health model.

DO provide Windows PowerShell scripts to assist in the administration of one’s service, especially to monitor performance counter values and data exposed through the WMI provider, and to monitor and manipulate the service through the administrative service contract that all services should implement.

DO document the facilities provided by the PowerShell scripts in the health model.

DO cover the activity tracing and message logging facilities of the Windows Communication Foundation in the health model.

DO include in the health model information about the security auditing events that the Windows Communication Foundation emits into the Windows Event Log.

DO integrate the health model into an integrated management environment familiar to administrators, such as Microsoft Operations Manager, IBM Tivoli, or HP OpenView. Otherwise, provide a custom management console for the service.

A health model defines what it means for a system to be operating normally and for it to be operating abnormally, and explains how to transition the system from an abnormal state back to a normal one. The Windows Communication Foundation provides many performance counters that should be very useful in detecting normal and abnormal states, as well as a WMI provider that not only offers additional information for that purpose, but some control points for restoring normal operation. The performance counters and WMI provider are disabled by default for security so that no information about a Windows Communication Foundation Service will be exposed that is not exposed deliberately. The performance counters and WMI provider can be turned on through configuration:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>

  <diagnostics
              wmiProviderEnabled="true"
              performanceCounters="ServiceOnly"/>
  </system.serviceModel>
</configuration>

Only the performance counters at the service level should be activated in general. Those at the endpoint and operation level should be used only to diagnose actual defects because they incur a considerable performance overhead.

When the WMI provider of a service has been activated, information about it can be retrieved through many WMI-enabled management tools, including Windows PowerShell. For example, this Windows PowerShell command will list the names of all the active Windows Communication Foundation endpoints on the local machine that are exposed through WMI:

get-wmiobject endpoint -n rootServiceModel |ft name

Also useful for diagnosing problems in order to restore a service to a healthy state, and therefore worthwhile covering in the health model for the service, are the activity traces that a Windows Communication Foundation service can emit to show its internal operations, and its ability to log messages. Those facilities are also activated through configuration as shown in Listing 21.4. A Service Trace Viewer tool is provided for studying activity traces and message logs.

Example 21.4. Activating Activity Tracing and Message Logging

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true"
                      maxMessagesToLog="300"
                      logMessagesAtServiceLevel="true"
                      logMalformedMessages="true"
                      logMessagesAtTransportLevel="true" />
    </diagnostics>
  </system.serviceModel>
  <system.diagnostics>
    <sources>
      <source
                name="System.ServiceModel"
                switchValue="Verbose,ActivityTracing"
        propagateActivity="true">
        <listeners>
          <add
                        type="System.Diagnostics.DefaultTraceListener"
                        name="Default">
            <filter type="" />
          </add>
          <add name="xml">
            <filter type="" />
          </add>
        </listeners>
      </source>
      <source
                name="System.ServiceModel.MessageLogging">
        <listeners>
          <add

                        type="System.Diagnostics.DefaultTraceListener"
                        name="Default">
            <filter type="" />
          </add>
          <add
                        name="xml">
            <filter type="" />
          </add>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add
                initializeData="C:logsServiceTraces.svclog"
                type="System.Diagnostics.XmlWriterTraceListener"
        name="xml"
        traceOutputOptions="Callstack">
        <filter type="" />
      </add>
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
</configuration>

Other data for determining the health of a service that the Windows Communication Foundation provides are the security audit events that it records in the Windows Event Log. That facility is also controlled by configuration, as shown in Listing 21.5.

Example 21.5. Configuring the Auditing of Security Events

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
        name="DerivativesCalculator.DerivativesCalculatorServiceType"
        behaviorConfiguration="DerivativesCalculatorService">
        <endpoint
          address="Calculator"
          binding="netTcpBinding"
          contract="DerivativesCalculator.IDerivativesCalculator"
        />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior
          name="DerivativesCalculatorService">
          <serviceSecurityAudit
            auditLogLocation="Application"
            suppressAuditFailure="false"
            serviceAuthorizationAuditLevel="SuccessOrFailure"
            messageAuthenticationAuditLevel="SuccessOrFailure" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Working with Windows Communication Foundation Clients

DO use the asynchronous pattern for operation contracts in Windows Communication Foundation clients.

Assume that a service implements this operation contract:

[OperationContract]
string Echo(string input);

Then the operation contract used by the client could be expressed according to the asynchronous pattern in this way:

[OperationContract(AsyncPattern = true)]
IAayncResult asynchronousResult = BeginEcho(
        string input,
        AsyncCallback callback,
        object state);
string EndEcho(
       IAsyncResult asynchronousResult);

The operation would be invoked as shown in Listing 21.6. Understand that the exchange of messages here is synchronous, but the Windows Communication Foundation delivers the service’s synchronous response to the client asynchronously. Also note that the typed proxy, the instance of System.ServiceModel.ClientBase<T>, is kept open until the response from the service is processed. That is done to keep the synchronous channel of communication with the service open until the response from the service has been received.

Example 21.6. Using the Asynchronous Pattern

public class EchoClient: ClientBase<IEcho>: IEcho
{
        public EchoClient(string endpointConfigurationName):
                base(endpointConfigurationName)
        {
        }

        public IAayncResult BeginEcho(
                string input,
                AsyncCallback callback,
                object state)
        {
                return base.Channel.BeginEcho(
                        input,
                        callback,
                        state);
        }

        string EndEcho(
              IAsyncResult asynchronousResult)
      {
                return base.Channel.EndEcho(asynchronousResult);
      }
}

EchoClient client = new EchoClient("endpointConfigurationName");
client.Open();

client.BeginEcho(
        "Hello, World!",
        this.EchoCallback,
        client);

Console.WriteLine("Press any key after the service has responded.");
Console.ReadKey(true);

client.Close();

void EchoCallback(IAsyncResult asynchronousResult)
{
    string echo =
                ((EchoClient)asynchronousResult.AsyncState).EndEcho(
                        asynchronousResult);
    Console.WriteLine("Echoed: {0}", echo);
}

The asynchronous pattern could be used for the service’s operation contracts, too, whether or not the asynchronous pattern is used at all in the programming of the client. However, it is only really useful to do so if the service, in responding to requests from clients, will be using objects with their own asynchronous programming interfaces.

DO correctly handle those exceptions that should always be anticipated in Windows Communication Foundation client applications.

The exceptions that should always be anticipated in programming Windows Communication Foundation client applications are System.TimeoutException and System.ServiceModel.CommunicationException. The proper way of handling both of those types of exceptions is to call the Windows Communication Foundation client’s Abort() method. Calling the Close() method after either of those exceptions has occurred will cause another exception to be thrown. This guidance is illustrated in Listing 21.7. Note that operation-specific exceptions of the type System.ServiceModel.FaultException<T> derive from System.ServiceModel.CommunicationException, so the handlers for those exceptions should precede the handler for the base System.ServiceModel.CommunicationException.

Example 21.7. Properly Anticipating Exceptions

public class EchoClient: ClientBase<IEcho>: IEcho
{
        public EchoClient(string endpointConfigurationName):
                base(endpointConfigurationName)
        {
        }

        public string Echo(
                string input)
        {
                return base.Channel.Echo(
                        input);
        }

}

EchoClient client = new EchoClient();
client.Open();

try
{
     string echo = client.Echo("Hello, World!");
     client.Close();
}
catch (TimeoutException exception)
{
    client.Abort();
}
catch(FaultException<MyFaultException> exception)
{
     if(client.State != CommunicationState.Open)
     {
         client.Abort();
     }
     else
     {
         //Client is still usable.
     }
}
catch (CommunicationException exception)
{
    client.Abort();
}

DO call the Open() method of Windows Communication Foundation clients before attempting to use them.

Before messages can pass through a Windows Communication Foundation client to a service endpoint, the Windows Communication Foundation must construct a stack of communication channels to implement the hierarchy of communication protocols implied by the binding. That takes time.

The construction of the stack of communication channels is initiated by a call to the client’s Open() method. However, it is not necessary to call the Open() method explicitly. If the Open() method has not been called explicitly before the client is used to send a message, the Windows Communication Foundation will call the Open() method implicitly.

Calling the Open() method explicitly before attempting to use the client to send a message does tend to get the first message on its way more quickly, though. The reason is that the construction of the stack of communication channels initiated by the explicit call to the Open() method gets underway asynchronously, and has usually advanced somewhat by the time the client is used to send a message.

DO manage the lifetimes of Windows Communication Foundation clients correctly.

The Windows Communication Foundation must construct the stack of communication channels required to implement the hierarchy of communication protocols implied by a binding before messages can pass through clients to services, and that is time-consuming. Consequently, those communication stacks are precious resources and should not be disposed of idly.

With that in mind, consider the scenario in which browser clients make requests of an ASP.NET application, which services those requests via a Windows Communication Foundation client that communicates with a remote service. Such an application should be designed in such a way that a single instance of a multithreaded Windows Communication Foundation client services all the requests from every browser client. The stack of communication channels required by the Windows Communication Client for sending messages to the remote service should be constructed ahead of the first request by constructing the client and calling its Open() method. Then the client should be cached and reused for each request.

On the other hand, the stack of communication channels used by a client to send messages to a service represents a finite resource that should be released as soon as it is no longer required. Explicitly call a client’s Close() method to accomplish that.

Properly Anticipating Exceptions

AVOID scoping Windows Communication Foundation service hosts and clients with the C# using statement or the Visual Basic Using block.

Scoping Windows Communication Foundation service hosts and clients with the C# using statement or the Visual Basic Using block does not easily allow for the correct handling of the expected exceptions illustrated in Listing 21.5. More precisely, those syntactical devices imply that the service host’s or the client’s Dispose() method will be called as the service host or client goes out of scope, and the Dispose() method implicitly calls the Close() method that will throw an exception if a System.TimeoutException or a System.ServiceModel.CommunicationException has occurred.

Scoping a Windows Communication Foundation client with the C# using statement or the Visual Basic Using block also implicitly devalues the client’s communication stack, connoting that it is to be quickly disposed, rather than carefully cached.

DO use the Microsoft.Web.UI.UpdatePanel control that is among the facilities of the Microsoft ASP.NET 2.0 Ajax Extensions to build interactive web clients for Windows Communication Foundation services.

Ajax is the acronym for the Asynchronous JavaScript and XML technique for programming more responsive web applications. Rather than posting an entire HTML form to an HTTP server in response to a user’s input, and replacing or refreshing the whole form, Ajax applications exchange small quantities of data with the server invisibly and only update particular visual elements. Because of the difficulty of parsing XML with JavaScript, Ajax applications now more typically exchange data with the server in the JavaScript Object Notation (JSON) format, rather than XML. The requests to the server are usually accomplished using a scriptable XMLHttpRequest object provided by the browser.

Microsoft’s support for Ajax programming includes the Microsoft.Web.UI.UpdatePanel control that is included among the Microsoft ASP.NET 2.0 Ajax Extensions. That control is used as a container for any number of other ASP.NET controls. Developers can use JavaScript to trigger an update to the content of Microsoft.Web.UI.UpdatePanel controls, which causes them to exchange data with the server. When the data arrives at the server, whatever code the developer has provided to handle the update to the Microsoft.Web.UI.UpdatePanel control executes on the server. That code can read data from any of the ASP.NET controls on the page and update any of the ASP.NET controls contained by the Microsoft.Web.UI.UpdatePanel object.

Programming Ajax applications using the Microsoft.Web.UI.UpdatePanel control has two highly desirable consequences. Most importantly, it yields the effect for which Ajax is intended: interactive web applications that update quickly in response to users’ input, with exchanges back and forth with the server happening invisibly. However, it saves the developer from having to write much JavaScript. After the update to a Microsoft.Web.UI.UpdatePanel object is triggered, which might require a few simple lines of JavaScript, the Microsoft.Web.UI.UpdatePanel object handles the work of exchanging data with the server, and the developer’s code that updates the display is code written in the developer’s preferred .NET programming language, running on the server. Minimizing the dependence on JavaScript code executing in the browser is a boon for Ajax programming because such code is notoriously difficult to debug and manage.

As a tool for building interactive web clients for interacting with Windows Communication Foundation services, Microsoft.Web.UI.UpdatePanel controls send requests to the server that result in .NET event handlers executing. Those handlers can use Windows Communication Foundation clients to interact with services beyond the web application server, as shown in Figure 21.1.

Using a Microsoft.Web.UI.UpdatePanel object as a client of a Windows Communication Foundation service.

Figure 21.1. Using a Microsoft.Web.UI.UpdatePanel object as a client of a Windows Communication Foundation service.

A common question is how to have Ajax code executing in the browser use the Windows Communication Foundation to exchange secure messages directly with remote services. That sort of interaction is quite different from the exchange of data depicted in Figure 21.1, wherein the Microsoft.Web.UI.UpdatePanel object interacts with a server running code that in turn connects to a remote service via the Windows Communication Foundation.

There are several things to know in considering that question:

  • There is no way of executing Windows Communication Foundation code within a standard browser.

  • Although it is possible to have code executing in a standard browser communicate with services deployed on some arbitrary host, doing so violates the same origin policy. The same origin policy is “an important security measure for client-side scripting [that] prevents a document or script loaded from one ‘origin’ from getting or setting properties of a document from a different ‘origin’” (Wikipedia 2006).

  • One can use the Microsoft Ajax Library, the client-side scripting counterpart to the server-side Microsoft ASP.NET 2.0 Ajax Extensions, to manually exchange data directly with a service at the same origin. The Microsoft.Web.UI.UpdatePanel control just saves one from having to expend the effort of doing that.

  • With the next release of the Windows Communication Foundation, concurrent with the next version of Microsoft Visual Studio, it will be possible for that service at the same origin with which the client-side script communicates directly to be a species of Windows Communication Foundation service—a species that can exchange JSON messages with XMLHttpRequest clients. Again, given that the Microsoft.Web.UI.UpdatePanel control saves one from having to write client-side script to communicate with services, the need for being able to write services that can exchange JSON messages with XMLHttpRequest clients is questionable.

  • Communication between client-side scripts and the server via XMLHttpRequest objects can be secured using the Secure Sockets Layer (SSL) protocol over HTTP.

  • It cannot be secured using any existing standard message security protocol because there simply is no such protocol for XMLHttpRequest objects and JSON.

Working with Large Amounts of Data

DO determine exactly how large input and output messages can be.

Working with Large Amounts of Data

DO NOT provide operations that might return indefinite, potentially very large quantities of data in response to requests.

Providing operations that might return very large quantities of data in response to requests could severely compromise the throughput of a service. Moreover, the base class for Windows Communication Foundation transport-binding elements, System.ServiceModel.Channels.TransportBindingElement, has a configurable MaxReceivedMessageSize property, and if the size of a message exceeds the value assigned to that property, the receiver will fail. The default value of the property is 64KB, and the maximum value is 264.

DO consider using the System.Net.FtpWebRequest class, rather than the Windows Communication Foundation, to transfer large amounts of data.

Service-oriented programming and development platforms for facilitating it, such as the Windows Communication Foundation, are meant for sending messages. Messages are meant to be like the things that get sent via postal services. Those are usually small enough to fit in envelopes that are just a little larger than the average adult’s hand. On good, albeit infrequent days, the postal service does indeed deliver somewhat bigger and sturdier things from Amazon.com. Yet, although there is evidently considerable variation in the sizes of messages sent through the post, one can expect to pay dearly and even get into some trouble if one was to, say, change residences, and attempt to move all of one’s belongings through the mail. One should also hesitate to send a lot of very sizeable things via a service-oriented programming technology.

By contrast, the File Transfer Protocol (FTP) is a venerable protocol that has been used for decades to transfer data of varying sizes and is widely supported on computing platforms. It supports authentication, and when used in conjunction with the SSL protocol, both the credentials for authenticating requestors and the data provided in response to requests can be transferred securely.

The System.Net.FtpWebRequest class allows one to write code that communicates with FTP servers to both uploading and downloading data. The EnableSsl property can be set to true to communicate via the SSL protocol with FTP servers that are configured to support that protocol.

DO use the Windows Communication Foundation’s implementation of the SOAP Message Transmission Optimization Mechanism (MTOM) for messages with binary constituents that must be transferred securely between platforms.

Binary data can be incorporated into XML messages for exchange across platforms via the Base64 Transfer-Content-Encoding. However, that encoding increases the size of the data by a third. According to MTOM, the binary data is first incorporated into the XML message using the Base64 Transfer-Content-Encoding, and then the protocols for securing XML messages are applied. Then the binary data is restored to its original format, and added along with the XML message to an XML-binary Optimized Packaging (XOP) package for transfer. The receiver reconstructs the secure XML message from the XOP package.

All the Windows Communication Foundation’s HTTP bindings have a MessageEncoding property to which the value System.ServiceModel.WSMessageEncoding.Mtom can be assigned to signify that MTOM is to be used. In that case, byte arrays and types derived from System.IO.Stream will be treated as binary data in the MTOM encoding process.

DO use a streaming transfer mode for messages that are too large to be buffered.

The Windows Communication Foundation buffers messages in their entirety by default. Doing so is essential to the application of certain protocols, WS-Security in particular. When the exchange of messages need not be secure, one can avoid the adverse implications for the application’s working set that are entailed by the buffering of entire messages by using a streaming transfer mode. That option is available with the HTTP, TCP, and named pipes transport channels. Assigning the value System.ServiceModel.TransferMode.Streamed to their TransferMode properties causes byte arrays and types derived from System.IO.Stream that are incorporated in the message to be transmitted in chunks of a configurable size. The message is not buffered in its entirety, although the maximum size of the message is still restricted to the value assigned to the transport channel’s MaxReceivedMessageSize property.

The option of using a streaming transfer mode is more useful if the data incorporated in the message is in a format that can be consumed as a stream. Such formats include those for digitally encoding audio and video. Being able to use a streaming transfer mode to avoid buffering the entire message in transmission is less valuable if the entire content of the message must be buffered anyway by the receiver for it to be processed.

DO use a channel for chunking messages that are too large to be buffered, but which must be secured.

If the exchange of messages needs to be secure, but the messages are too large to be buffered in their entirety, one must provide a custom channel to break the messages into chunks and position that channel at the top of a Windows Communication Foundation channel stack. For each outbound message passed into it, the chunking channel would pass one or more messages representing chunks of the original outbound message into the channels beneath it. A channel implementing a secure messaging protocol that was lower down in the stack of channels could then secure each of those messages. On the receiving side, a corresponding channel would reassemble the original message from the chunks. Although there is no standard protocol for breaking messages into chunks and reassembling them, an ad hoc protocol for that purpose could certainly be implemented on any platform. A sample channel for breaking outbound messages into chunks and reassembling inbound messages from a series of chunks is included among the Windows Communication Foundation samples in the Software Development Kit for the .NET Framework 3.0. That sample relies on the chunks being transferred in order, and depends on the System.ServiceModel.Channels.ReliableSessionBindingElement to guarantee that ordering. That binding element is a constituent of the predefined NetTcpBinding and WSHttpBinding.

Debugging Windows Communication Foundation Applications

DO first thoroughly test and debug service types by invoking their methods directly, before proceeding to invoke their methods via the Windows Communication Foundation.

DO thoroughly test and debug services using Windows Communication Foundation clients deployed on the same computer system before testing and debugging services using clients on remote computers.

DO thoroughly test and debug service types by hosting them within .NET applications before deploying and testing them within IIS.

Testing and debugging services hosted by .NET applications is easier than testing and debugging services hosted within IIS because, in the latter case, debugging requires attaching the debugger to an IIS process, which can be laborious if it has to be done repeatedly.

DO rely on the Windows Communication Foundation samples in the Software Development Kit, especially when debugging.

The Windows Communication Foundation samples provided by the .NET Framework 3 Software Development Kit cover a great many scenarios, they have considerable documentation, and they do work. So, when one’s own Windows Communication Foundation solution is not working properly for no apparent reason, systematically compare it to the corresponding samples to identify the problem. If the defect is not apparent by inspection, make a copy of the sample and modify it, proceeding step-by-step to match one’s solution, and testing the modified sample after each step. That method will reveal the problem.

DO, in debugging, use the ReturnExceptionDetailInFaults property of the System.ServiceModel.ServiceDebugBehavior class to have services include .NET exception information in faults returned to clients.

Debugging Windows Communication Foundation Applications

DO NOT use that option in production.

Setting the value of the ReturnExceptionDetailInFaults property of the System.ServiceModel.ServiceDebugBehavior to true will cause the Windows Communication Foundation to include .NET exception information in any faults that a service returns to its clients. Having that information can be useful in debugging, although having it exposed in production compromises security.

DO use message logging and activity tracing in debugging.

Summary

This chapter offered more than 80 points of guidance for using the Windows Communication Foundation. The guidance covers approaches to adopting the technology, how to work with endpoints, how to work with services and clients, how to design exchanges of large amounts of data, and how to debug.

References

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset