This documentation is archived and is not being maintained.

Design Considerations for Using WCF in Complex Business Communications, Part 1

Visual Studio 2010

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

This topic contains the following sections.

When you design a business's communications system, you often must begin with a complex environment that includes disparate systems that operate over a variety of protocols, with different message formats and conflicting security constraints. Unfortunately, many of these systems do not support standard HTTP web services. In addition, many client sites do not conform to standard designs or have clear cut standards for quality of service. Consequently, it is up to you, the developer, to design a way for these systems to communicate with each other. WCF provides a flexible solution that can handle many of these scenarios, but one constraint is that the systems must still communicate with some application programming interface (API). The solution is to use WCF's extensibility points. These allow you to tailor the inputs and outputs to each component, while still taking advantage of the benefits offered by the API. This article deals specifically with communications to nontandard hosts and clients, and does not cover other integration issues such as how to data load flat files.

A nonstandard host is any host that does not expose its API over an industry-standard Simple Object Access Protocol (SOAP) interface. Simply because a host is nonstandard does not mean that you must develop new web service adapters. There are many good point solutions where clients talk directly to the host over an exposed API. A point solution is an application that is developed to meet a specific need, without considering any future projects or applications that might need to use this application. The following figure illustrates a simple point solution.

Referenced Image

However, when you create an enterprise solution that supports multiple clients, or single clients that cannot communicate over an exposed API, you must consider alternative designs.

To interact with a nonstandard host, base your solution on the Object-Oriented Adapter pattern. The Adapter pattern is a design pattern that allows two classes to communicate that could not otherwise interact because their interfaces are incompatible. From an object-oriented design perspective, your solution usually creates a class that translates from one interface to another. This perspective expands the meaning of an adapter to include not only the translation of data from one format to another, but also to include transformations of elements such as a protocol, or security measures. An example of such an adapter is one that translates between the Open Database Connectivity (ODBC) interface and the SOAP interface.

Design Questions:

When you analyze a development project, it is important to identify, early in the process, all the communication points. These communication points often are the high risk areas for development and integration projects. Many projects are delayed, or fail, due to an inability to connect the hosts. Hosts with complex interactions should be integrated into the project as soon as possible. A solid development strategy includes plans to catalog all of the system interactions. For any nonstandard hosts, it is a best practice to determine the most effective ways to deal with each interaction that the nonstandard hosts have with other system components. The following questions can help you to determine the approach to take, and the level of effort required to integrate these hosts.

The following questions seem obvious, but many developers have invested countless hours reinventing the wheel. If a good, reusable API already exists that works for all clients, do not invest additional time and money in a new approach. As you catalog system interactions, you should also catalog all the ways that the host exposes its API.

  • Is the nonstandard host actually a standards-based host?

  • Specifically, for example, does it expose a SOAP standard API for consumption?

  • Does the host already expose an API that can be consumed by all clients?

  • Do adapters already exist for your nonstandard host, or are there third- party adapters available?

For example, as part of the Microsoft® BizTalk® Adapter Pack, Microsoft has released a number of ready-to-use adapters. The BizTalk Adapter Pack includes adapters for connectivity to SAP, Oracle E-Business Suite, Microsoft® SQL Server® data management software, Siebel and Oracle databases. The adapter pack can be installed in conjunction with, or separately from, BizTalk Server.

If there are no available adapters, you will probably need to develop custom adapters that meet your requirements.

  • Can all clients consume WCF services?

    If you have clients that use SharePoint® or BizTalk, then .NET applications can consume WCF services natively. Microsoft has an SDK for this scenario, which is named the WCF Line of Business (LOB) SDK, which you can download at http://www.microsoft.com/downloads/en/details.aspx?FamilyID=f8bb78c7-72c8-46ba-ad52-6cde4258bf37. This SDK provides a simplified programming model, and design tools.

  • How much of the API needs to be exposed?

    If your client only requires a subset of the API that is exposed by a nonstandard host, you do not need to write adapters for the entire interface.

Custom Application Development

If the answer to the first two questions in the previous section is "No," you must write custom service code to wrap the interactions with the nonstandard host. The following figure illustrates how this custom code, which is the adapter service, exists between the clients and the nonstandard host.

Referenced Image

This rest of this section describes some best practices for writing custom code.

Design for Future Versions and Enhancements

If there is one thing that is certain, it is that nothing is certain. When you design an adapter to a nonstandard host, remember that change is inevitable. A good design that can accommodate change is essential.

Wrapping Request and Response Parameters

Consider the following method call from a nonstandard host.

public bool CreateCustomer(string firstName, string lastName, string phoneNumber);

If you mapped this operation exactly as is to an operation contract, you might have a service operation that looks like the following code.

[OperationContract]bool CreateCustomer(string firstName, string lastName, string phoneNumber);

What happens when the next version changes requirements for the nonstandard host, and adds an email address to the CreateCustomer operation? The new operation signature may look like this.

public bool CreateCustomer(string firstName, string lastName, string phoneNumber, string emailAddress);

If your original wrapper for this API has a one-to-one mapping, you cannot add a new parameter to the operation without losing compatibility with every client. You have no choice but to create a new operation with the additional parameter, such as is shown in the following code.

[OperationContract]bool CreateCustomerV2(string firstName, string lastName, string phoneNumber, string emailAddress);

Alternatively, you can create the same operation name on a new endpoint. However, imagine trying to manage hundreds of different operations with two or more versions. This option quickly becomes unmanageable.

Clearly, a better approach is needed. Instead of wrapping method calls one for one, spend a little more time at the beginning of your development process, and wrap request and response parameters in their own classes. Your design will be much better able to accommodate change. This approach is shown in the following code.

[DataContract]
public class Customer
{
    [DataMember]
    public string FirstName { get; set; }

    [DataMember]
    public string LastName { get; set; }

    [DataMember]
    public string PhoneNumber { get; set; }

}

[DataContract]
public class CreateCustomerRequest
{
    [DataMember]
    public Customer Customer { get; set; }
}

[DataContract]
public class CreateCustomerResponse
{
    [DataMember]
    public bool Success { get; set; }
}

[ServiceContract]
public interface ICustomerService
{
    [OperationContract]
    CreateCustomerResponse CreateCustomer(CreateCustomerRequest request);
}

If you define specific data contracts for the customer object, and for the request and the response phases of the CreateCustomer method, you can now add new fields to any of those data contracts without breaking compatibility with existing code. While this approach allows you to add new fields without changes to the existing code, it does not take care of all breaking changes. For example, changing the data type of a field from a string to an integer would still break compatibility with existing code. For this reason, have a service versioning strategy in place as early in the software development life cycle as possible. (Service versioning is outside the scope of this article.)

Enumerations

While enumerations can make client code more readable, they make it difficult to maintain compatibility when they are used in responses. If a service returns an enumeration that the client does not understand, a serialization error is raised on the client. You cannot add a new enumeration on a response without breaking compatibility with clients. An alternative is to return a primitive data type such as int, and to provide a lookup service that finds the relevant information about that response code. Consider the following code.

[DataContract]
public class CreateCustomerResponse
{
    [DataMember]
    public bool Success { get; set; }

    [DataMember]
    public int ErrorCode { get; set; }
}

It may seem logical to make ErrorCode an enumeration, so that the clients can take advantage of language features such as switch statements. However, ErrorCode is something that is likely to change in the future. It makes more sense to make it an int, but the client cannot know what to do when the integer is returned. The following code shows an additional lookup operation that corrects this problem.

[OperationContract]
GetErrorCodeInformationResponse GetErrorCodeInformation(GetErrorCodeInformationRequest request);

Wrap Only the Pieces You Need

A nonstandard host will certainly change over time because of new features, or requirements. Only write custom code to wrap operations that the clients actually use. It is relatively easy to add new operations later, and limiting your code also decreases some of the risks associated with versioning.

When You Must Break Compatibility

There are several approaches possible if a new version of the API for a nonstandard host is released, and you absolutely must break XML compatibility. The first approach is to create a new operation with a different name. Use this approach if there are only one or two changes to operations. Follow the DRY principal (Do not Repeat Yourself), and have the older method call the new operation. This reduces the likelihood of problems that can occur when you manage two code bases.

The other approach is to create a new version of the service that will run alongside the older version. Use this approach is there are extensive changes that will break compatibility with the clients. As with the first option, previous versions of the operation should be modified to call the new versions.

No matter which approach you use, always plan the deprecation of previous versions. If you do not, the service architecture will, over time, become convoluted. A deprecation plan often requires that you negotiate with the owners of the client applications to decide when they will make upgrades. You must also verify that the old versions are no longer in use. Windows Server® AppFabric allows you to monitor which operations are being used. You can download it at http://www.microsoft.com/downloads/en/details.aspx?FamilyID=467e5aa5-c25b-4c80-a6d2-9f8fb0f337d2. If you know how many calls are being made to each operation, you can better plan how to deprecate them. For more information about versioning of services, see Additional Resources at the end of this document.

WCF Line-of-Business SDK

Use the WCF Line-of-Business SDK to create your own binding types, adapters, and, in general, to simplify what can be a complex development process. The SDK does require that you implement not only the wrapper, but also the capabilities to browse, search, and retrieve metadata. If you only need to wrap a few methods, then the SDK is probably not the best choice because it increases the complexity of your solution. However, if you must wrap many method calls to a nonstandard host, and you also know that your clients will all be either a Microsoft product such as BizTalk, or SharePoint, or a .NET client, this may well be the right approach for you.

To implement full metadata support, you must implement the following interfaces.

In addition to the metadata interfaces, you must also write the actual implementation of your wrapper. To do this, you generally need to implement the IOutboundHandler interface. This is documented on TechNet at http://technet.microsoft.com/en-us/library/microsoft.servicemodel.channels.common.ioutboundhandler.aspx. This interface only contains the Execute method. Keep in mind that the SDK provides placeholder stub versions of all of the classes, which makes it much easier to locate the methods you want.

Even if you use the WCF Line-of-Business SDK to create an implementation, you may still have an Execute method that looks like the following code.

switch (message.Headers.Action)
            {
                case "Echo/EchoStrings":
                    return ExecuteEchoStrings(om as ParameterizedOperationMetadata, message, timeout);
                    
                case "Echo/EchoGreetings":
                    return ExecuteEchoGreetings(om as ParameterizedOperationMetadata, message, timeout);

                case "Echo/EchoCustomGreetingFromFile":
                    return ExecuteEchoCustomGreetingFromFile(om, message, timeout);
            }

(This code is taken from step 7 of the "Develop an Echo Adapter" tutorial, which is on TechNet at http://technet.microsoft.com/en-us/library/bb798099(BTS.70).aspx.)

The equivalent code could be created with custom code, with no requirement to implement all of the metadata interfaces. The SDK-generated code offers no advantages. It requires all the metadata support, and still cannot be consumed by non-Microsoft clients.

To harness the real power of the SDK, combine it with reflection code. You can use reflection when you implement the metadata providers to parse through the API, and build the XML for the metadata. You can also use reflection to implement the Execute method. While this approach is a great deal of work for only a few methods, it saves a large amount of time when there are, for example, one hundred operations.

For a tutorial on how to use the WCF Line-of-Business SDK, see the "WCF Line-of-Business Adapter SDK Tutorials" on TechNet at http://technet.microsoft.com/en-us/library/bb798114(BTS.70).aspx.

The BizTalk Adapter Pack

The BizTalk Adapter Pack is a group of ready-to-use adapters that were created with the WCF Line-of-Business SDK. It includes adapters for a number of commonly used nonstandard hosts such as SAP, Siebel eBusiness Applications, Oracle eBusiness Suite, Oracle Database, and SQL Server. Note that the adapter pack can be used with technologies other than BizTalk Server. These adapters can be consumed by any Microsoft client that can consume WCF services, and they require no code for point-to-point connection from BizTalk Server, SQL Server and custom .NET applications to key LOB systems, such as SAP R/3, Siebel, and Oracle. The following figure illustrates how a BizTalk adapter mediates between two Microsoft technologies and the SAP system.

Referenced Image

Because the adapter pack creates the WCF wrappers, development time is shorter, which, in turn means a faster time to market and reduced costs. For more information, see the BizTalk Adapter Pack page at http://www.microsoft.com/biztalk/en/us/adapter-pack.aspx.

Show: