Showing posts with label Contracts. Show all posts
Showing posts with label Contracts. Show all posts

Friday, February 20, 2009

WCF: Message Headers and XmlSerializerFormat

Sometimes you need to use the classic XmlSerializer due to interoperability or when doing schema first based on XSD contracts that contains e.g. XML attributes in complexTypes. I've used the [XmlSerializerFormat] switch on services many times without any problems, but recently I had to make use of a custom header - and that took me quite some time to get working.

This is the wire format of the header:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" >
<s:Header>
<h:CorrelationContext xmlns:h="urn:QueuedPubSub.CorrelationContext" xmlns="urn:QueuedPubSub.CorrelationContext" xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<CorrelationId>c4e03aae-9501-46e9-bbb8-a9ddf6c4fe15</CorrelationId>
<FaultAddress>feil</FaultAddress>
<Priority>0</Priority>
<ResponseAddress>svar</ResponseAddress>
</h:CorrelationContext>
. . .

The WCF OperationContext provides access to the message headers:

CorrelationContext context = OperationContext.Current. IncomingMessageHeaders.GetHeader<CorrelationContext> (Constants.CorrelationContextHeaderName, Constants.CorrelationContextNamespace);

I had used [XmlElement] attributes to control the name and namespace of the [Serializable] class members, only to get this error:

'EndElement' 'CorrelationContext' from namespace 'urn:QueuedPubSub.CorrelationContext' is not expected. Expecting element 'CorrelationId'.

That really puzzled me. To make a long story short, the MessageHeaders class and GetHeader<T> method only supports serializers derived from XmlObjectSerializer, such as the DataContractSerializer - but not XmlSerializer. To make this work, your header class must be implemented as a [DataContract] class even for [XmlSerializerFormat] services.

My working message header contracts looks like this:

[DataContract(Name = Constants.CorrelationContextHeaderName, Namespace = Constants.CorrelationContextNamespace)]
public class CorrelationContext
{
[DataMember]
public Guid CorrelationId { get; set; }
[DataMember]
public string FaultAddress { get; set; }
[DataMember]
public int Priority { get; set; }
[DataMember]
public string ResponseAddress { get; set; }
}


[MessageContract(IsWrapped = true)]
[Serializable]
public abstract class MessageWithCorrelationHeaderBase
{
[MessageHeader(MustUnderstand = true, Name = Constants.CorrelationContextHeaderName, Namespace = Constants.CorrelationContextNamespace)]
[XmlElement(ElementName = Constants.CorrelationContextHeaderName, Namespace = Constants.CorrelationContextNamespace)]
public CorrelationContext CorrelationContext { get; set; }
}

This code was made and tested on .NET 3.5 SP1.

Tuesday, February 10, 2009

SOA: Business Event Message Models

InfoQ has published an article about SOA Message Type Architecture by Jean-Jacques Dubray. The article shows how to model 'message type' artifacts based on an enterprise common data model using a DSL, and also outlines how the modeled artifacts can be used to generate XML schemas for use in your service contracts. The message type DSL is not for modeling messages, it is just for modeling the types used as message payloads. In WCF terms, a message type is a [DataContract].

Note that even if the message type model contains a set of standardized verbs, the model does not cover business process aspects such as flow and actions. You will still need to analyze the business processes that pass those message types around to model the action, query and notification events that drive the processes. I've written several times about creating such a model that comprise both the business events and the message types, a business process information model (BPIM). I like JJ's approach; I just think that we need to model also the business capabilities and interactions that utilize the message types to get a complete set of artifacts for service contracts.

One difference is that I prefer using a common information model (CIM) as the basis for modeling the message types, rather than an enterprise data model (EDM). It is a lot of effort to create an EDM that covers all information in all systems-of-record in a company; and the moment you have completed the all-encompassing model, your CxO will inform you that parts of the business have been outsourced or that a new business will have to be incorporated, or even just that the CRM system is to be replaced. Change is the only constant. Thus I prefer starting small by creating a CIM that covers only the business entities comprised by the business processes that are about to be service-oriented. As there will be multiple resource domains in your architecture, there will be multiple CIM models as your SOA grows. Federate these domain models by creating context maps for cross-domain capabilities and logic only.

Create a CIM according to the Domain-Driven Design (DDD) principles and use the domain entity objects when implementing the business logic underlying your services. Design the BPIM based on the CIM, ensuring that the model is canonical for each process domain. As the BPIM is a projection of the CIM, the service interface messages will have an unambiguous mapping to/from the underlying domain resources (entities and aggregates). Mapping data between the message types and objects will be required in the service implementation (provider container).


The 'message type' artifacts should also be partitioned into bounded contexts according to business area as in Domain-Driven Design; where each service domain comprise a set of cohesive services based on the same underlying CIM, delivering an BPIM for the specific business domain. Having clear domain boundaries make it easier to analyze, model and design, and version the artifacts - it also aids the discoverability by the consumers by providing a clear business context for the provided services.

Your SOA solution will, as it grows, encompass multiple service domains each with its own BPIM. Composite services that compose business capabilities across two or more domains will require mediation between the message models. Even if "transformation avoidance" is considered a SOA best practice, it is unrealistic that you will be able to avoid mediation completely in your service bus (composition container). In addition, you cannot expect to enforce your model upon the extended enterprise, think of e.g. third-party and outsourced capabilities.


The message types exposed by a service domain implicitly become the "canonical schema". They enable better service discoverability, reuse and composition as they all share the same underlying data model - which also ensures that the message payloads have common semantics within the service domain. In combination with business functions, the schemas provide a complete standardized service contract.

As you have seen, the BPIM and CIM approach fits well with DDD. It also fits well with the middle-out SOA approach - including the recommendation to start small and think big picture, not big bang.

Sunday, February 01, 2009

Service Compatibility - A Primer

In a comment on the InfoQ article Contract Versioning, Compatibility and Composability about my definition of service forwards and backwards compatibility, the problem of talking about compatibility of services compared to the definition of schema compatibility is acknowledged.

The "problem" is that a service version that is compatible with the specifications of older versions of the service, can be achieved using both backwards and forwards compatible schemas. That is correct, but doesn't preclude having a definition of forwards and backwards compatibility for service providers, a.k.a "services". Service compatibility is based on ability to validate a message, it is not based on using wildcards in the schema definition.
For a definition of the three types of forward compatibility, see my post on schema, service and routing compatibility.

Seen only from the service provider perspective, how it handles incoming messages is what defines if a service is forwards or backwards compatible (or both). How consumers handle messages sent by the service is really not of any concern for the provider - wait, read on.

Thinking about this within SOAP 1.x constraints, where a WSDL operation has a request message and a response message with fixed schema definitions/version (unilateral contracts), will lead to the conclusion that operations cannot be classified as forwards or backwards compatible, only the message schemas. This is a limitation of SOAP, but not of messaging in general.

In the following examples, the v1.2 service provider is backwards compatible and interacts with a v1.1 consumer. However, the schemas are not designed to be forwards compatible - they do not support XML extensibility (schema wildcards). In this scenario, the consumer can do either XSD validation of response messages against the v1.1 schema, or do 'validation by projection' of response messages - i.e. do "ignore unkown" validation. Doing 'validation by projection' is a recommended practice for compatibility and really simplifies building SOA solutions - this is also how WCF validates messages. So how to handle consumers that only do XSD validation, without relying on schema wildcards?

In REST, the consumer can put an "accept formats" header in the v1.1 request message, and the service provider can then respond with a v1.1 schema even if the service version is v1.2. The service provider adheres to it's obligations by being backwards compatible, and the consumer is allowed to express it's version expectation - the service has bilateral contracts.

Service Virtualization is a mechanism that can help with service versioning. The task of such an abstract endpoint intermediary is to handle versioning through both service compatibility and schema compatibility. A virtual service supports multiple versions of the service on the same endpoint, and must be capable of processing older requests. The virtual endpoint must have a mechanism that allows for the latest major v1.2 provider to handle v1.1 consumers. The intermediary mediates between the schema versions by transforming or projecting/enriching the messages as needed.

Back to the example, the service provider v1.2 response message can be stripped down to a v1.1 message by the intermediary as it is sent back to the consumer. The net effect is that the service has virtual bilateral contracts.

In messaging in general, by definition there are no duplex channels, only one-way channels (see
Enterprise Integration Patterns by Hohpe/Woolf). On top of this, you can have a logical two-way channel for message exchange patterns such as request-response, specified using a "reply-to" address and a "reply-format" (bilateral contracts). The message compatibility is defined by the schema constructs, but just as in REST, the version of the incoming message does not dictate the version of the response message. It is the implementation of the endpoint that processes the messages that defines the compatibility policy of the endpoint, not the schemas.

So, service compatibility do not require using forwards compatible schemas in addition to backwards compatible schemas. The message validation policy is what defines service compatibility.

It is of course much simpler to just have a service compatibility policy based on that the schemas used in the services must be both forwards and backwards compatible - as shown in the "SOAP-style unilateral contracts" service compatibility figures.



Click figures to enlarge.

This way, the service provider or consumer platform need not handle "request-format" and "reply-format" that have different versions. In such a unilateral schema compatibility policy world, services are just intrinsically compatible through schema compatibility.

Tuesday, January 13, 2009

SOAMM: Published on InfoQ

A followup article to the article on "Contract Versioning, Compatibility & Composability" is now published at InfoQ. This new article is about showing how the recommended contract design policies from the versioning article relate to Microsoft's SOA Maturity Model (SOAMM), and at the same time suggest a roadmap for achieving these capabilities.

SOAMM Overview:


SOAMM Capabilities:

Click to enlarge pictures.

Related resources:
Unum Case Study
SOAMM web-cast

I recommend the web-cast for a good overview of the maturity model.

Friday, December 19, 2008

REST Versioning: The Ripple Effect

A few weeks ago, I posted an illustration of the SOA service + schema versioning ripple effect for incompatible or sematic changes. Stepping out on a limb, I thought I should make a similar illustration for the artifacts in a RESTful service.

The artifacts in such a service is based on the four REST principles:

1. Identification of resources
2. Manipulation of resources through representations
3. Self-descriptive messages
4. Hypermedia as the engine of application state (HATEOAS)

Even REST needs a versioning and compatibility policy. The only thing that is not subject to change is the uniform interface. All other artifacts are subject to semantic or incompatible changes as the service evolves over time. Changes to the flow of the state machine are changed semantics, while changes to the decisions (allowable next state transitions/hyperlinks) need not be.


The ripple effect is a bottom-up effect, and an incompatible change on e.g. a customer address resource will cause an explosion of new versions of all affected representation artifacts. In the end, such a change should propagate even to the service consumers. If not, the consumers would operate on representations that have different real-world effects than they are expected to have.

Semantic changes should always be explicitly communicated to the consumers. Incompatible changes should be treated the same way for consistency, i.e. enforce a uniform versioning policy that allows consumers to be standardized.

Having a versioning strategy let you control the effects of the inevitable changes. Using a compatibility policy will help you alleviate some of the negative effects of versioning.

Thursday, December 11, 2008

Service Compatibility: Backwards, Forwards

The definitions for backwards compatible and forwards compatible contracts are straightforward:
  • A new version of a contract that continues to support consumers designed to work with the old version of the contract is considered to be Backwards Compatible

  • A contract that is designed to support unknown future consumers is considered to be Forwards Compatible
Backwards compatibility is typically achieved by using optional schema components, while forwards compatibility is typically achieved by using schema wildcards. What can be confusing is that schema compatibility is strictly defined as being between message sender and receiver, while it now is more common to talk about service consumers and service providers.


The correct definition of forwards compatible schemas as defined by David Orchard is this: "In schema terms, this is when a schema processor with an older schema can process and validate an instance that is valid against a newer schema". Backwards compatibility means that a new version of a receiver can be rolled out so it does not break existing senders. Forwards compatibility means that an older version of a receiver can consume newer messages and not break.

The confusion is caused when applying this message based definition to services, as service operations typically are both receivers and senders of messages. Most services should be designed to support both backwards and forwards compatible messages. But are the services themselves backwards or forwards compatible?

I define service compatibility this way:
  • A new version of a service that continues to support consumers designed to work with the old version of the service is considered to be Backwards Compatible

  • A service that is designed to support unknown future consumers is considered to be Forwards Compatible

Backwards and forwards compatible services use a combined “ignore unknown/missing” strategy, that is a combination of forwards and backwards contract (schema) compatibility. The following figures illustrates the definition of forwards and backwards services.


As can be seen from the above figure, service backwards compatibility depends on the provider being able to validate the on-the-wire request XML against a newer schema version and the consumer being able to validate the response XML against an older schema version. The provider must be able to "ignore missing", while the consumer must be able to "ignore unknown".

As can be seen from the above figure, service forwards compatibility depends on the provider being able to validate the on-the-wire request XML against an older schema version and the consumer being able to validate the response XML against a newer schema version. The provider must be able to "ignore unknown", while the consumer must be able to "ignore missing".

So my advice when talking about compatibility is, always make it clear if you're focusing on the contracts (message) or the services (provider). You can even talk about compatibility from the consumer perspective if you're bold enough. But please: never, ever talk about the service provider as the message consumer...

To add to the complexity of forwards compatibility, there are three types of forward:
  • Schema forward compatibilty
  • Service forward compatibilty
  • Routing forward compatibilty, a.k.a implicit versioning
In service version routing, the service endpoint accepts multiple versions of the contract (service virtualization) and then applies one of two routing policies:
  • Implicit version routing: forwards compatible service routing, where a message automatically is routed to the newest compatible version
  • Explicit version routing: traditional service routing, where each message is routed based on explicit version information in the message, typically a namespace

The implicit version routing policy is what our InfoQ article refers to as forwards compatible service versioning.

[UPDATE] More details on service vs schema compatibility.

Tuesday, December 09, 2008

Published on InfoQ: SOA Versioning

An article about "Contract Versioning, Compatibility & Composability" in service-oriented solutions that I've written together with Jean-Jaques Dubray has been published on InfoQ. It covers a lot of themes that I've written about in this blog, and focuses on the need for having a versioned common information model for the enterprise data that comprise the messages used in your business processes.

Thursday, November 27, 2008

Service+Schema Versioning: The Ripple Effect

In my SOA versioning, compatibility & composability session at NNUG this week, I stressed the importance of recognizing the ripple effect that incompatible or semantic changes to service contract artifacts will have. This illustration captures how versioning an artifact will affect upstream artifacts:


The ripple effect is a bottom-up effect, and an incompatible change on e.g. a customer address schema will cause an explosion of new versions of all affected contract artifacts. In the end, the change will propagate even to the service consumers.

Having a versioning strategy let you control the effects of the inevitable changes. Using a compatibility policy will help you alleviate some of the negative effects of versioning.

Wednesday, November 12, 2008

Service+Schema Versioning: Flexible/Strict Strategy

In a few weeks time I will be giving a session at NNUG on SOA service and schema versioning strategies and practices. A central topic will be schema compatibility rules, where I will recommend creating a policy based on the "Strict", "Flexible" and "Loose" versioning strategies described in chapter 20.4 in the latest Thomas Erl series book Web Service Contract Design and Versioning for SOA. I guess David Orchard is the author/editor of part III in the book.

I recommend using a “Flexible/Strict” compatibility policy:

  • Flexible: Safe changes to schemas are backwards compatible and cause just a point version

  • Strict: All unsafe schema changes must cause a new schema version and thus a new service version

  • Do not require forwards compatible schemas (Loose, wildcard schemas) - schemas should be designed for extensibility, not to avoid versioning

  • Service interfaces should also have a Flexible/Strict policy
Safe changes is typically adding to schemas, while unsafe changes are typically modifying or removing schema components.

Note that forwards compatible schemas is not required to have forwards compatible services, as service compatibility is defined by the ability to validate messages. WCF uses a variant of 'validation by projection' (ignore unknown) for forwards compatibility, but also supports schema wildcards.


As Nicolai M. Josuttis shows in the book SOA in Practice (chapter 12.2.1 Trivial Domain-Driven Versioning), even simple backwards compatible changes might cause unpredicted side effects such as response times breaking SLAs and causing problems for consumers. It is much safer to provide a new service version with the new schema version, as if there is a problem, only the upgraded consumers that required the change will be affected.

Note that even adding backwards compatible schema components can be risky, but adding is typically safe. Josuttis recommends using "Strict" as it is a very simple and explicit policy, but I prefer "Flexible/Strict" as this gives more flexibility and less service versions to govern.

Avoid trying to implement some smart automagical mechanism for handling schema version issues in the service logic. Rather use backwards compatibility, explicit schema versions and support multiple active service versions. In addition, consider applying a service virtualization mechanism.

Thursday, April 17, 2008

WCF: Design of Client Domain Objects

The client Domain Objects [Evans] (aka business entity - BE) must be related to the data contracts provided by the web-services. Still, they will need to be enhanced with e.g. dirty-tracking, validation, state, property conversions (xs:date), business rules, and other aspects needed to produce a fully working client application. This article describes how to achieve this when sharing domain objects across layers is not an option (i.e. interop with JEE services), and the same applies to AOP.

There are two models for this: annotating the BEs with XML serialization attributes to decorate the WSDL data contract, or creating separate Mapper classes [Fowler] in addition to the BE classes. The mapper will then map to and from the service proxy DTO objects [Fowler] at run-time. Note that the mapper model will be required whenever the BE is significantly different from the data contracts. Thus, BEs should be implemented using the first model to the maximum extent possible, only using the second model for advanced mapping requirements.

The decorator option is easier, performs better, and gives less code to maintain - but requires knowledge of XML serialization and will not give compilation errors when the data contract changes (even if the proxy is updated). Good integration test coverage is needed to make this option viable, i.e. to detect that the DTO's XML payload has changed.

Easier, less code: Only one BE class to implement and maintain as the BE is a Decorator object [GoF] wrapping the "DTO" XML. Note that there is no auto-generation of these classes and that there are no mappers or DTO objects. The BE classes are shared directly by the service proxies.
Performs better: There will only be one de-serialization step directly into the BE because it wraps the XML directly. This also applies to any nested child objects and collections - there will be no looping code to create and map the BE structure. The same applies to sending BE objects to the services - they will be directly serialized into "DTO" XML.
Contract change risks: As the BE classes are not auto-generated from the data contracts, changes to the contracts will not be automatically reflected in the serialization attributes on the BE properties. As (de)serialization happens at run-time, any mismatch between the XML and the serialization attributes cannot be detected by the compiler. Thus, adequate integration tests are required to detect that BE properties are not being set/read during (de)serialization. Breaking data contract changes will require that the serialization attributes are updated accordingly.

The mapper option is also quite easy, but performs worse and requires more code to be implemented and maintained (two classes: BE and mapper). However, it requires no knowledge of XML serialization and it will give compilation errors when the proxy + DTO objects are updated after data contract changes. Note that good unit testing is required to ensure that the mappings to and from the DTO objects are correct, including the complete object graph (nested child objects and collections).

Quite easy, more code: Two classes to implement and maintain, one BE class and one related mapper class. The mapper class must cover nested child objects and collections, both to and from the DTO objects. The DTO objects are auto-generated and will never be manually maintained by developers.
Performs worse: In addition to the de-serialization step of the DTO objects, the service agent must execute the mapping code to fill the BE object with data from the DTO object. Add to this extra transformation looping through nested child objects and collections, and the performance gets proportionally worse with the complexity of the object graph.
Contract change risks: The DTO objects are auto-generated based on the WSDL and are thus always in synch with the data contracts. Any breaking changes to the DTO classes will cause the mapper classes to give compilation errors. Breaking data contract changes will require that the mapper classes are updated accordingly - both to and from the DTO classes.

To complement this BE design approach, read the programming models for the Decorator option and the Mapper option to learn about the technical details and issues of each option.

Thursday, October 04, 2007

Enterprise-Level Business Concepts

Dan North has expanded on Arjen Poutsma's SOA 'holiday request form' analogy in his post 'A Low-Tech Approach to Understanding SOA'. Applying the paper form analogy to design the interactions and messages of your business processes is an approach that I really recommend.

What got my attention was the recommendation at the end of the post in the 'Avoid a universal domain model' section: Do not introduce an “enterprise information architecture” / “universal data dictionary” trying to force everyone to use the same domain model. Instead, introduce business concepts. This is effectively the higher-level, ubiquitous language that ties together all of the finer-grained domain models behind each service. The services use the enterprise-level business concepts when interacting, which decouples the service consumer from the service provider and allows them to evolve independently.

This is spot on with my advice for making a business process business process information model (BPIM) and avoid enforcing a "enterprise data model" or "canonical schemas" across your services. Not only does BPIM allow for loosely-coupled, evolvable services; it also allows for the services to be semantic covenants which is important for service composition.

Thursday, June 14, 2007

Semantic Covenant: The Service is Always Right

This post is about service semantics and how to be “liberal in what you accept” in your service-oriented architecture implementation. Let me start by defining that in this post the term "service" is used for a specific business capability that can be composed and reused in different business processes. I.e. the idiom “service” used here is representing a building block business capability. This is not to say that a service equals a single business process step, it just makes it easier to talk about the topic of this post (which is not BPM): semantic covenants.

A service has a defined contract, but also some implied semantic. The semantic is hard to express through the contract, and the business compositions that utilizes (consumes) the operation can only assume that the service is a covenant, i.e. that the service will do B when provided with input A. Having semantic coupling is just another type of coupling that prevents services to be truly loosely-coupled and that will cause a breaking ripple effect through all consumers of the service when the inevitable thing happens: change.


There is some thinking in the SOA community that to avoid this ripple effect caused by a service contract or semantic change, it is up to the service to do the right thing independent of the information provided by the sender of the message. That is, that the service should behave as if the consumer is always right, and adapt its semantic to suit the sender.

A service can do no such thing, as it is an actual representation of a specific business capability. The capability does what it does, i.e. a credit check is just that – a credit check. It must be up to the business composition of the services to interpret the real-life events that have occurred and their context, and then invoke the right set of operations. Note that the service should still be liberal in what it accepts (message duck-typing), as it is the business event that is important, not the format of the event message.

It is easier to recognize that the service is always right when you think of messages as representing business events and apply EDA thinking such as publish/subscribe instead of a habitual SOA command and control pattern. A service will then subscribe to business events that must trigger the business capability, but the service need not know who the publisher is. Neither need the publisher know who is listening for the business process state changed events (event topics). Thinking in service “consumers” is very connected to having a request/reply, command and control style SOA. 

To simplify the “service is always right” notion, let us call the publisher a “consumer”. The consumer can send any message (business process event, context and state; mail-order analogy) it likes, as it is the publisher. However, as the consumer does not know who the subscribers are, it cannot know or depend on the services having specific semantic. The service cannot depend on who the publisher is, it just accepts that a business event happened that it must process. A business event message never specifies any handling operations, the logic to decide how to handle the event is part of the mechanism that routes messages to services. This ensures that the autonomous and loose coupling tenets of SOA are fulfilled.


When you apply EDA style thinking to service orientation, you’ll see that the service is always right. There cannot be any traditional “the consumer is always right” in relation to service semantic, as the services have no request/reply consumers.

Alas, still the consumer can still be right; it is just the responsibility of the process composition mechanism to accept what ever messages (events) the consumer (publisher) throws at it and invoke the applicable service operations. It is the business process composition mechanism that has to be the semantic covenant.

PS! Note that I have deliberately used the term "composition" here and not even mentioned using an ESB or BPM to implement this. The semantic covenant 
composition mechanism can simply be implemented as a mashup (composite application) instead of a composite service or an orchestration/saga.

Monday, June 04, 2007

Flex support for [Flags] enum - not

My last post showed how to get explicit semantics in a WCF contract description using [Flags] enum. As readers of my blog may know, our main service consumer is a Flex 2.x client. As it turns out, Flex has an issue with correctly interpreting such enumerations.

A [Flags] enum looks like this in the WSDL:

<s:simpleType name="DisciplineFlags">
<s:list>
<s:simpleType>
<s:restriction base="s:string">
<s:enumeration value="None" />
<s:enumeration value="Administration" />
. . .

The correct input format for an element that uses the above type is XML whitespace separated values, such as "None Administration". Flex, on the other hand, interprets the list schema construct too litterally, and tries to pass in an array. This causes an exception in the WCF service, as the message cannot be deserialized due to the incorrect XML.

As the Flex guys had no other solution to this problem, I just added a simple "FlexDisciplineFlags" string property to the contract. Flex has to pass a CSV list of the filter enum values, and the service will "cast" the input into the official filter condition:

private void HackFlexIssues(DocumentCardFilter filter)
{
if (String.IsNullOrEmpty(filter.FlexDisciplineFlags)==false)
{
//input string must be a CSV-list of enum items
filter.DisciplineFlags = (DisciplineFlags)Enum.Parse(typeof(DisciplineFlags), filter.FlexDisciplineFlags, false);
}
}

Note that I'm kind of making a workaround for a technical limitation, maybe I'll get sued by Adobe :)

Friday, June 01, 2007

Enum Flags, Specification Pattern & Explicit Semantics

We use the specification pattern for all the dynamic query operations in our WCF service, and this includes using Nullable<T> on all criteria elements and several "multiple choice" criteria based on [Flags] enumerations. To ensure that our contracts convey explicit semantics, we have used long meaningful names in the enumerations instead of the more cryptic codes that are wellknown in the internal system and that are the domain terms used by the biz-people. These internal codes is not, however, very meaningful to the partners that are consuming the WCF service.

As the multi-choice lists sometimes contain 20+ items, I feared that interpreting the [Flags] enum
and building the repository query would become huge switch statements with lots of ugly bit-wise "and" logic to deduce which of the 20+ items were specified in the criteria. In addition, I needed a way to add the translated code items to a SQL @parameter list as part of a SQL in clause to actually filter records on the criteria.

As I never embark on implementing something ugly, I decided to look for a simpler way of interpreting the [Flags] enum. Knowing that an enum internally is represented by the integers and not the human-friendly names, I decided to add another internal enum that exactly mirrors the contract enum, just using the internal domain codes, and then cast to the internal enum. But the DataContractSerializer has a simpler approach using the [EnumMember] attribute:

[DataContract]
[Flags]

public enum DisciplineFlags
{
None = 0,
. . .
[EnumMember(Value = "
Instrumentation ")]
I = 0x000100,
[EnumMember(Value = "
MarineOperations")]
J = 0x000200,
[EnumMember(Value = "
Materials")]
M = 0x000400,
[EnumMember(Value = "
Navigation")]
N = 0x000800,
//
[EnumMember(Value = "
Process")]
P = 0x001000,
[EnumMember(Value = "
QualityManagement")]
Q = 0x002000,
[EnumMember(Value = "
Piping")]
S = 0x004000,
[EnumMember(Value = "
Telecommunications")]
T = 0x008000,
. . .
}

Note that you must apply the [DataContract] attribute to the enum for the [EnumMember] to take effect. If you just use the plain enum as a [DataMember] property, then the internal names will be published in the contract.

The "None" item is there as the default value for the DataContractSerializer when the incoming XML specification contains no flags filter element (xsi:nil="true"). The nullable flags filter must be asserted like this before usage:

if (filter.DisciplineFlags.HasValue
&& filter.DisciplineFlags != DisciplineFlags.None)

{
. . .
}


So now I had the real one-letter domain codes, but how to avoid bit-wise interpretation ? Also, I needed a list of the codes making up the flags combination. The answer is really simple:

string disciplineList = filter.DisciplineFlags.ToString();
string[] inValues = disciplineList.Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries);

The last problem is that you cannot add the list of values for an in-clause as a @parameter. Just concatenating the CSV-string into the SQL is not an option due to SQL-injection attacks. So a for-loop is needed to add SQL parameters and related values:

for (int i = 0; i < inValues.Length; i++ )
{
string param = "@in" + i;
if (i > 0) sql += ", ";
sql += param;
filterCommand.Parameters.AddWithValue(param, inValues[i].Trim());
}

I would rather have avoided the for-loop, but at least the specification interpreter became much simpler than I expected it to be. No pesky switch statements and bit-wise logic.

See the end of this earlier post for further details about implementing specification filters using TableAdapters.

Thursday, March 01, 2007

WCF: Validating Message and Data Contracts using VAB

Last year I wrote about how we used a 'broken rules' engine and the Noogen validation component for doing validation across all layers including the business logic and the WinForms client. Using a validation engine allows for better design and separation of concerns in your components, and makes the code easier to understand as the validation logic is clearly separated from your domain logic.

These days I have started to use the Validation application block (VAB) from the upcoming Enterprise Library 3 provided by the patterns & practices team at Microsoft. VAB works just like our custom 'broken rules' engine, it allows you to declaratively add validation rules as [attributes] to your objects instead of implementing the rules as code; and then passing an object instance to the Validation.Validate() method to interpret the declared rules and provide a list of those rules that are broken, i.e. the rules that the object instance does not comply with.

The nice feature provided by VAB is that you can actually completely separate the validation rules from your domain logic code by allowing you to declare the rules in an external XML config file instead of adding [attributes] on your domain objects. It is really nice as there is a VS2005 editor tool that allows you to create and edit the rules instead of hand-coding XML. The figure shows how easy the editor makes it to e.g. choose which objects and properties to validate (click to enlarge):


Remember to set the "default rule set" property for each type/class that you add. Forgetting to set it will give no warning, but neither will your rules be applied unless you actually specify a rule set name in the call to
Validation.Validate() (more on this problem below).

By having the validation rules in config, you can actually modify the rules without having to recompile and redeploy your solution to keep up with the ever changing business requirements.
Read more about this and other VAB features at David Hayden's blog. Also read his four part VAB introduction series to see some code.

I use VAB to validate the incoming message and data contracts of our WCF service. I have decorated all the message and data contracts with VAB attributes in addition to the WCF attributes. This allows for validating just only a single data contract or for validating the complete structure of a message including contained data contracts. The capability of validating an object graph in a single operation is what I love most about VAB. These are the object graph validators:
  • [ObjectValidator] is for validating a composite child object of the current object
  • [ObjectCollectionValidator(typeof(...))] is for validating a collection of child objects
Combine the object validators with the [NotNullValidator] to make the composite objects required. Note that there seems to be a bug in the current VAB beta when using the collection validator on recursive structures (e.g. folders containing folders), your app-domain will just silently die. [UPDATE] More about this bug here.

All messages derive from a common base class that ensures that all messages contain the same set of [MessageHeader] info required by our service. This allows us to validate all messages using a single method:

public static void ValidateMessage<T>(T message)
where T : DefaultMessage, new()
{
ValidationResults results = Validation.Validate(message);

//NOTE: DO NOT EXPOSE IMPLEMENTATION DETAILS
if(results.IsValid==false)
{
FaultContracts.DefaultFaultContract fault = new FaultContracts.DefaultFaultContract();

fault.ErrorId = (int)EApprovalErrorCode.InvalidMessage;

fault.ErrorMessage = Utilities.MakeDetailedErrorMessage(results);

throw new FaultException<DefaultFaultContract> (fault, new FaultReason(InvalidMessageReason));
}
}

Note that as VAB uses generics in the
Validation.Validate(<T>) method, the code must ensure that type fidelity is not lost before calling the method. I.e. you must use a generic <T> type input and not the base class DefaultMessage in your message validator method input. The problem by using a base class type input is that even if you pass a derived class at run-time, the compile-time type will be used to deduce the validation rules - thus only the base class will get validated.

Using VAB gives us better control over the validity of the input than the DataContractSerializer is able to provide. E.g. it allows for checking string lengths and controlling that string elements are actually not just empty values; and validating e-mail addresses, guids, etc; rules that cannot be expressed using WCF attributes. These are some of the field level validators:
  • [NotNullValidator] ensures that required fields are not null; just got to love this, no more String.IsNullOrEmpty() checks in my domain logic
  • [StringLengthValidator(min, max)] controls the string length; must be combined with [IgnoreNulls] for non-required strings; no more 'field would be truncated' exceptions
  • [RegexValidator] validates the field against a regular expression; useful for validating e-mail addresses, phone numbers, guids, etc.
  • A guid validator example, with optional { and }: [RegexValidator(@ "^(\{){0,1}
    [0-9A-F]{8}\-[0-9A-F]{4}\-[0-9A-F]{4}\-[0-9A-F]{4}\-[0-9A-F]{12}
    (\}){0,1}$", RegexOptions.IgnoreCase)]
  • E-mail validator for 99% of all the possible formats: [RegexValidator(@ "^[A-Z0-9._%-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$", RegexOptions.IgnoreCase)]
Note that you must apply the validators to public class members only; validators on private class members will not be interpreted, and neither will they cause an exception.

Validators can be composed into Boolean expressions using [AndCompositeValidator] and [OrCompositeValidator]. In addition, there are plans to provide cross-property validation in the final release of the validation application block. This will allow for comparing class members against each other instead of just literals, values sets and type/conversions. Sometimes, however, this is not sufficient for complex rules.

When your rules are too complex to be supported by the standard validators, VAB allows you to do self validation by providing [SelfValidation] methods:

[HasSelfValidation]
[MessageContract]
public class ImportProjectDocumentsMessage : DefaultMessage
{
[SelfValidation]
public void ValidateFileXorData(ValidationResults results)
{
//XOR operation
if ( (_fieldRootNode == null ^ String.IsNullOrEmpty(_fieldImportXmlFilename) ) == false)
{
results.AddResult(new ValidationResult("One of the parameters ProjectDocuments or ImportXmlFilename must be specified", this, "ImportProjectDocumentsMessage", null, null));
}
}
. . .
}

All classes containing self-validation must be marked with the [HasSelfValidation] attribute. VAB allows you to mix standard validators with self-validation. You just got to love this kind of flexibility.

Note that as there will be no exceptions if your validation rules are not interpreted by VAB for some reason (such as the above lost type fidelity problem, missing "default rule", etc), I recommend hard-coding one of the rules in your logic to ensure that such mishaps are detected during development and testing. This fail-safe can be enclosed in #if DEBUG so that it is not included in your final solution. Regardless, you should always have a unit-test expected to fail when validating; if the test does not fail, you know that the rules are not being interpreted. Use [ExpectedException] or Assert.IsFalse() to verify that invalid input actually causes the validation to fail.

Download the EntLib3 Feb2007 CTP beta including VAB from CodePlex. Read the documentation and check the 'quick start' samples to get started.

[UPDATE] Watch the 'Taking Advantage of the Enterprise Library in Your Site' web-cast for an introduction to EntLib3.

[UPDATE] Watch the 'New Capabilities in Enterprise Library 3.0' web-cast for even more details about the new features in EntLib3.

Another part of enterprise library 3 to look forward to, is the Policy Injection block. Read more about PIAB at Tom Hollander's blog. This looks good for adding cross-cutting concerns such as logging and exception handling to the domain logic without actually modifying the logic to add non-domain stuff. Alas, this is in fact just 'aspect oriented programming' (AOP) under a new name - read Patrik Löwendahl's comments and the response from the p&p team at his blog.

[UPDATE] Also check out the new WCF stuff in the Orcas March CTP: integration of WF and WCF with new activity types and a new WorkFlowServiceHost; plus JSON endcoding and webHttpBinding for better REST + POX/JSON support.

Thursday, February 01, 2007

WCF: Importing InfoPath forms to Data Contracts

The solution that we are implementing includes a WCF service that allows a set of document metadata to be imported into our system. The operation is ImportProjectDocuments( ImportProjectDocumentsMessage request) and the message contains the ProjectDocuments data contract specifying the document structure. The data contract of course adhers to my 'separate structure from data' rule. The import operation is intended for application-to-application layer usage by our e-biz partners, for connecting our systems in a service-oriented distributed architecture.

Then came the need for allowing users to manually enter the document structure
in a disconnected manner and submitting it to our service later on; i.e. a human-to-application layer service.

Enter InfoPath 2003 as the occasionally connected client (OCC), a perfect match for our existing data contract (see closing note) and a nice data entry application. Note that InfoPath is not used to submit the entered data directly to the WCF service endpoint, just to fill out a form and submitting it to our server as XML files - by e-mail or to a SharePoint form library when connected. You may think of the form library as the human friendly "message queue".


By the way - having a OCC service consumer is a rather good assessment of whether your services adheres to SOA best practices such as explicit boundaries, message based, share contract, and self-contained business event operations based on the paper form metaphor.


The submitted InfoPath forms must then be processed, typically by a workflow system such as K2.NET or even WWF, monitoring the form library. Each XML form must taken off the "message queue", deserialized using the DataContractSerializer and passed to
our service, invoking the correct operation for the submitted XML data.

This is all quite trivial, but even if the XML generated by InfoPath adheres to the XSD schema, InfoPath adds some processing instructions that the deserializer chokes on:

System.Runtime.Serialization.SerializationException:
There was an error deserializing the object of type DNVS.DNVX.eApproval.DataContracts.ProjectDocuments.
Processing instructions (other than the XML declaration) and DTDs are not supported.

You must remove the InfoPath processing instructions before deserialization, and the easiest way to do this is by using a standard XmlTextReader instead of the WCF XmlDictionaryReader used in the MSDN documentation:

[Test]
public void DeserializeDataContractFromInfoPathXmlFileTest()
{
string fileName = @".\form1.xml";
ProjectDocuments documents = null;

DataContractSerializer xlizer = new DataContractSerializer(typeof(ProjectDocuments));

FileStream fs = new FileStream(fileName, FileMode.Open);
//NOTE: must get rid of InfoPath processing instructions
//XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());

XmlTextReader reader = new XmlTextReader(fs);

documents = (ProjectDocuments) xlizer.ReadObject(reader, true);

reader.Close();
fs.Close();

Assert.IsNotNull(documents, "Could not deserialize the project documents XML");
}


The ReadObject() method is what actually converts the XML into a WCF data contract instance, ready for processing by the service layer just as if it had arrived through the WCF endpoint.

Note that the duration data type defined in the XSD schemas generated by WCF will cause an InfoPath 2003 parse error. You need to remove it before you start to design a new InfoPath form based on the data contract XSD schemas.

Friday, January 19, 2007

WCF: Core categories of data contracts

One of the famous SOA tenets is "services share contract, not class/implementation", meaning that it is the schema of your contract that is the main conveyor of how to consume the operations provided by your service. This has a huge impact on how you should design your contracts to provide for clear, understandable and comprehensive semantics, and also to minimize ambiguity in how to use your service. Contracts that have subtle or vague semantics are just more difficult to use and are thus more error prone. The same applies to contracts that are too flexible.

This post is about how to design data contracts that a simple to use, rather than easy to implement (simple vs easy); and at the same time keeping the number of data contracts to a minimum. The latter is important both for the consumers of your service and for the maintainability of your service. It is also important wrt SOA governance, the less stuff you have to govern, the better. Less schemas, less semantics, less maintenance, less governance.

Data contracts belong to one of these two groupings: altering state and querying information. Generally speaking, operations that modifies your system need to comply with stricter requirements and rules than operations that reads data from your system. This is because operations that can leave your system in an invalid state have greater technical impact on your business than operations that just returns information. Of course, if you disclose the incorrect information, your business could be in serious legal trouble.

The two data contract groupings can be further refined into several categories based on the different needs for expressing contract semantics and for being unambiguous. These five data contract core categories have manifested themselves through several more or less service-oriented solutions that I have implemented:
  • Insert/update contracts: Typically one contract per domain object. Optional contained data contracts must be avoided or specifically handled.
  • Delete contracts: Typically one contract per domain object.
  • Specification/criteria contracts: Typically one contract per result contract, but it is not uncommon that a single specification can relate to multiple result contracts. Optional members are perfectly standard; the same applies to nullable criteria. Composite specifications are normal.
  • Read/query result contracts: One or more contracts per domain object. Optional contained data contracts are allowed for flexibility and this is a key mechanism for keeping the number of result contracts to a minimum. Composite contracts are also allowed for the same reasons.
  • Batch update/import contracts: Typically one contract per domain object batch operation type. Composite contracts are normal. Optional composite or contained contracts must be specifically handled.
These are core data contract categories for entity/core services. You will need to have more than just these core data contracts to provide good, event-driven, specialized business process services (EDA) in different contexts (sales, support, accounting, logistics, partners, suppliers, customers, etc).

The term ‘domain object’ also comprises complex objects (aggregate root objects) such as an order or a document card. The term ‘contained’ is used for complex objects. The term ‘composite’ is used for contracts that consists of several domain objects. The term ‘batch update’ includes insert, update and delete actions or a combination of these actions.


Note that I use CRUDy terms in the categories for simplicity (easier for me), to cover any real-life event that affects the state of a domain object. E.g. the “customer has moved” event falls into the “update” category.

A result contract will typically contain a composite structure of domain objects, defined by exactly the same unambiguous data contracts used for insert/update actions. The main reason for defining data contracts in the first place is to promote standardization and reuse across services and operations. To be able to support both the rigid insert/update data contact requirements and the flexible result contract requirements; it becomes a must to separate structure from data, isolating the structure/composition to the result set data contracts. Structural elements in a data contracts implicitly impose subtle semantics: how will the service handle the omission of composite/contained domain objects.

Insert/Update Contracts

It is important that insert/update contracts have little room for ambiguity, especially for complex domain objects. E.g. if the customer data contract contains a collection of addresses, what will happen if a customer update action is performed and no addresses are provided: does it mean that the customer no longer have any addresses or does it just mean that your can update a customers phone number without having to specify the addresses?

Such contained objects must be either A) required or B) specifically handled and by default optional/ignored. Controlled optional elements can be handled the way that the .NET 1.x XmlSerializer handled optional elements: using an extra property to indicate the state of the optional element. The XmlSerializer uses a Boolean XxxSpecified property for each optional element, e.g. OrderShippedDateSpecified.

Rather than using just a Boolean for the contained optional object, I recommend using an enumeration that contains Ignore (default value) and then some other applicable actions; much like cascading actions in SQL Server. The customer data contract should contain both an AddressList collection and an AddressListAction enumeration with e.g. the values Ignore, Replace, Alter, Purge. The point is that the user has to explicitly assign an action on the contained collection, rather than the service just assuming that an empty collection means deletion of the existing children. Assumptions are semantic coupling, and that is something you should strive to avoid.

Note that these 'insert/update' contract recommendations apply to entity/core services, which are not the services you want to expose publicly. Your public services need to reflect the events of your service-oriented business processes, and these "published" services belongs to the 'application to application services' category. By layering your services according to the four service categories,
you will be able to expose more specialized operations with smaller contracts. Large contracts imply stronger coupling to the service, and as large contracts are more likely to change, your service will be more subject to breaking changes. Small contracts are simpler contracts, and simple contracts are important for the reusability, reliability, quality and robustness of your service (more about this in "Patterns for High-Integrity Data Consumption and Composition" by Dion Hinchcliffe).

You can still provide a very specific business operation that builds on the core service. E.g. the "customer has moved" event can be supported by an composite operation that takes only the customer key and the new postal address; which internally gets the complete customer, alters the address, and then stores the customer, in a single transaction using the core services.
Services at the A2AS layer allows you to be "liberal in what you accept" as they shield the consumers from the details of the core services.

Read/Query Result Contracts

Result data contracts should be able to fit multiple needs and support several views of domain objects and composite result sets. At the same time, a consumer should be able to control how much information that gets returned from the service. E.g. one consumer might not be interested in address information when fetching customer data. Thus, a result data contract will most likely comprise optional elements, and consumers will not fail if some data is not present in the result set.

An empty collection does not normally imply the same ambiguity for reads as it does for insert/update contracts. A consumer will typically assume that if a fetched complex object contains no elements for a contained data contract, then the object does not have any such children; e.g. that a customer has no addresses if the customer AddressList collection is empty. An extra metadata property could be added to the result data contract as an indication of whether an optional element actually contains data even if not returned due to the processed query specification.

Note that ‘not present’ in the result set is not the same as ‘missing’ from the result set, which is clearly an error and should have caused a service fault.

Batch Update/Import Contracts

Batch update contracts are typically used to alter the state of a set of (related) domain objects. E.g. to update the TaskList collection of a project by sending a message that contains the tasks to add, modify and remove as one batch. Batch operations are a good way to avoid having to expose transactions outside your service; package all domain objects that must be altered in a transaction into a single message and perform the update using a single transacted operation.

Note that each data contract must still follow the rules described for ‘insert/update contracts’ even when used as part of a batch contract.

To be ideal objects for batch operations, domain objects should expose a “row-state” property; if they don’t, you need something like ‘Service Data Objects’ to make your batch contracts really simple to use. A message with one collection per action should be the last alternative.

Thursday, January 11, 2007

WSSF v2 released: WCF contract first, contract versioning

Normally I wouldn't post just some links, but the release of the WCF web-service software factory version 2 merits an aggregation post.

Read about WSSF v2 details such as contract first supprt (WSCF), versioning guidance, etc, at Don Smith's blog; and download it from MSDN (not GotDotNet). The complete web-service versioning emerging guidance article is a must-read for anyone implementing "published" services.

Boy am I excited to check out the WSCF stuff, I just loved the WSCF tool provided by Christian Weyer/thinktecture a few years ago.


Note that the data contract wizard still assigns order attribute values incrementally, and not according to the best practices recommended by Microsoft:

"The Order property on the DataMemberAttribute should be used to make sure that all of the newly added data members appear after the existing data members. The recommended way of doing this is as follows: None of the data members in the first version of the data contract should have their Order property set. All of the data members added in version 2 of the data contract should have their Order property set to 2. All of the data members added in version 3 of the data contract should have their Order set to 3, and so on. It is okay to have more than one data member set to the same Order number."


Read Aaron Skonnard's Service Station article 'The Service Station for WCF' at MSDN for an introduction to the WCF WSSF.

Note that if you do not use VSTS, some of the new features such as the "WCF semantic code analysis" tool will not work (not even if you have VS2005 Pro + FxCop 1.35).

Thursday, December 21, 2006

Consume WCF BasicHttpBinding Services from ASMX Clients

The interoperability of WCF services has been touted quite a bit: just expose an endpoint using BasicHttpBinding and you will have a WS-I Basic Profile adherent web-service. This is of course backed by some simple examples of math services, all RPC-style and none of them messaging-style services.

We have for some time been consuming our BasicHttpBinding WCF services from a Flex 2 client and a WinForms smart client using a WCF generated client proxy (svcutil.exe). Just to be certain that our partners would have no problem setting up an application-to-application integration; I decided to test our services ASMX-style using "Add web reference" in Visual Studio (wsdl.exe). This to ensure that the services are consumable from non-WCF systems, i.e. systems without the .NET 3 run-time installed.

Well, surprise, surprise; it wasn't as straightforward as shown in the examples. There are several details that you need to be aware of, and some bugs in the service/message/data contract serializing mechanism.


I started by creating a new WinForms application and just used "Add web reference" to reference my "projectdocumentservices.svc?WSDL" file and WSDL.EXE generated an ASMX-style proxy for me. So far this looks good. I then added code to call my HelloWorld method on the proxy, which gave me this run-time error:

System.InvalidOperationException: Method ProjectDocumentServices.GetDocumentRequirement can not be reflected. ---> System.InvalidOperationException: The XML element 'KeyCriteriaMessage' from namespace 'http://DNVS.DNVX.eApproval.ServiceContracts/2006/11' references a method and a type. Change the method's message name using WebMethodAttribute or change the type's root element using the XmlRootAttribute.

Note that the Flex 2 client has no such problems with the BasicHttpBinding web-service, thus it must be related to how the generated proxy interprets the WSDL.

My service uses the specified message in several operations:

[System.ServiceModel.OperationContractAttribute(Action = "GetDocumentCard")]DocumentCardResponse GetDocumentCard(KeyCriteriaMessage request);

[System.ServiceModel.OperationContractAttribute(Action = "GetDocumentCategory")]DocumentCategoryResponse GetDocumentCategory(KeyCriteriaMessage request);

[System.ServiceModel.OperationContractAttribute(Action = "GetDocumentRequirement")]DocumentRequirementResponse GetDocumentRequirement(KeyCriteriaMessage request);

I turned to MSDN for more info and found the article 'ASMX Client with a WCF Service' (note the RPC-style math services) which lead me to 'Using the XmlSerializer'. So, accordingly, I added the [XmlSerializerFormat] attribute to the [ServiceContract] attribute in my service interface. Then I compiled and inspected the service, and got a series of different errors when trying to view the generated WSDL:

[SEHException (0x80004005): External component has thrown an exception.]

[CustomAttributeFormatException: Binary format of the specified custom attribute was invalid.]

[InvalidOperationException: There was an error reflecting type 'DNVS.DNVX.eApproval.DataContracts.DocumentCard'.]

Applying the well-known XML serialization attributes [Serializable] and [XmlIgnore] at relevant places in the data and message contracts helped me to isolate the problem to the use of collections and the WCF [CollectionDataContract] attribute.



To make a long story short, there is a bug in the WCF framework that affects .asmx but not .svc when using the [CollectionDataContract(Name="...")] attribute parameter:


"This happens only with Asmx pages but not for Svc due to a known Whidbey issue. Removing the "Name" parameter setting would work. If it is possible, you could also have your client instead use svc only."

Removing the Name parameter from all my [CollectionDataContract] attributes made all the WSDL reflection errors disappear, and I was again able to view the generated WSDL file. This time for a service that used the [XmlSerializerFormat] attribute.

Full of hope, I updated the web reference in my test client and ran it. Calling the test method lead me straight back to my original problem: the message contract used in multiple operations. Note that removing all but one of the operations makes the problem go away.


According to 'Message WSDL Considerations' WCF should have done this when generating the WSDL:

"When using the same message contract in multiple operations, multiple message types are generated in the WSDL document. The names are made unique by adding the numbers "2", "3", and so on for subsequent uses. When importing back the WSDL, multiple message contract types are created and are identical except for their names."

Inspecting the WSDL showed me that multiple XSD schemas are not generated. Certainly another WCF issue/bug. The problem can be solved by deriving the message class into new classes with unique names and using the derived classes in the operations. But when you have a message that is used ubiquitously, e.g. DefaultOperationResponse, this is not a viable solution.

As of now, our BasicHttpBinding WCF service cannot be consumed by ASMX clients. So much for interoperability...

[UPDATE] Read my new post about how to expose a WCF service also as an ASMX web-service.