I’ve been answering some questions on Google Groups dealing with ASP.NET web services. In some of the questions, people asked, “How do I send a <insert-user-defined-type-here> that’s in <insert-language-here> to a <insert-another-language-here> client?” Or, “Should I send a DataSet/XmlDocument/String to my client for my complex type?”. Clearly, there’s some confusion (or misunderstanding) about how types are represented in the service world.

The purpose of this post (along with the proceeding three) is to share the basics principals outlined by the tenets of Service Orientation. I will present the tenets in no particular order…the reason I started with Share schemas and contracts, not types and classes tenet is because it deals with the concept presented in the previous paragraph. Onward!

First of all, what is a contract? dictionary.com defines contract as to make an agreement. In code, one way of specifying a contract is by defining an interface that tells the rest of the system, “Hey, I define the following methods/properties/fields and I promise that my implementors will do the same.” In essence, the implementor can be any type in the system that has agreed to adhere to the definitions imposed by the interface.

For example, take this IBank interface that defines two methods, Credit and Debit.

public interface IBank
{
void Credit(int accountNumber, double ammount);
void
Debit(int accountNumber, double ammount);
}

Any type that calls the implementor of this interface can be certian that the two methods defined will be accessable to them through the interface. In other words, a code contract has been established between the two types. You might be telling yourself, “Ok, now tell me something I DON’T know.” The thing you might not know is that you’ve just implicitly defined a schema for operations of the contract. What is a schema? Once again, dictionary.com defines schema as a diagrammatic representation; an outline or a model. In other words, a defined layout…of what? Anything!

Take the Credit method from the above example. This method will communicate with the rest of the world through the use of an integer and a double. The simple schema for an integer is any signed/unsigned digit including zero without any decimal places. Now, how your world, or platform, interprets an integer, that’s a totally different thing. For example, the .NET interpretation of an integer is defined by the System.Int32 structure; the Java interpretation can be the java.lang.Integer type. You might be asking yourself, “what about a complex type? How is it going to be represented in a system different from mine?” Well…what’s its schema?

For example, say you have a type named Account,

public class Account
{
public int number;
public double
balance;
}

What does the type “represent”? Plain and simple, it’s nothing more than an encapsulation of an integer and a double. No tricks or hidden fees…just two basic types. If you think about it, a complex type is nothing more than a container for complex and basic types represented through a meaningful name.

If you plan on exchanging your complex types across systems, use a technology that will provide a system agnostic view of your contracts and their schemas. For example if you choose web services as your solution, you can count on WSDL to aid in the definition of your contract and XML Schema to define your … well, schemas.


Say you have redefined the above IBank interface and exposed it as an Indigo (WCF) service contract that looks like,

[ServiceContract(Name="Bank")]
public interface IBank
{
[OperationContract(Name
="GetAccount", Action="GetAccountRequest", ReplyAction="GetAccountResponse")]
Account GetAccount(
int accountNumber);
}

Using the ServiceContractAttribute and OperationContractAttribute classes, you add metadata to your contract to modify the generated WSDL contract. The WSDL contract for the IBank service contract look like this,

<wsdl:definitions name="Bank" targetNamespace="http://tempuri.org/"
xmlns:wsdl
="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns
="http://tempuri.org/"
xmlns:wsa
="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:soap
="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:message name="Bank_GetAccount_InputMessage">
<wsdl:part name="parameters" element="tns:GetAccount" />
</
wsdl:message>
<wsdl:message name="Bank_GetAccount_OutputMessage">
<wsdl:part name="parameters" element="tns:GetAccountResponse" />
</
wsdl:message>
<wsdl:portType name="Bank">
<wsdl:operation name="GetAccount">
<wsdl:input wsa:Action="GetAccountRequest" message="tns:Bank_GetAccount_InputMessage" />
<
wsdl:output wsa:Action="GetAccountResponse" message="tns:Bank_GetAccount_OutputMessage" />
</
wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BasicHttpBinding_Bank" type="tns:Bank">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<
wsdl:operation name="GetAccount">
<soap:operation soapAction="GetAccountRequest" style="document" />
<
wsdl:input>
<soap:body use="literal" />
</
wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</
wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Bank">
<wsdl:port name="BasicHttpBinding_Bank_port" binding="tns:BasicHttpBinding_Bank">
<soap:address location="http://localhost:1346/ContractSchema/Bank.svc" />
</
wsdl:port>
</wsdl:service>
</wsdl:definitions>

As you can see, the Indigo runtime has taken the extra information you provided and created the contract with your defined actions. The thing that is missing from this WSDL contract is the schema for the Account type. I removed this out from the contract to make the WSDL more readable. Now, any client that can understand WSDL and SOAP can connect to your service by exchanging information that adheres to your service’s contract.

To specify a schema for your Account type, you can use the DataContractAttribute and DataMemberAttribute classes to control the generation of the XML Schema for the type.

[DataContract(Name = "Account")]
public class Account
{
[DataMember(IsRequiredv
= true, Name = "Number", Order = 0)]
public int number;

[DataMember(IsRequired = false, Name = "Balance", Order = 1)]
public double balance;
}

The XML Schema generated for the type will look like this,

<xs:schema elementFormDefault="qualified"
targetNamespace
="http://schemas.datacontract.org/2004/07/"
xmlns:xs
="http://www.w3.org/2001/XMLSchema"
xmlns:tns
="http://schemas.datacontract.org/2004/07/">
<xs:complexType name="Account">
<xs:sequence>
<xs:element name="Number" type="xs:int" />
<
xs:element minOccurs="0" name="Balance" type="xs:double" />
</
xs:sequence>
</xs:complexType>
<xs:element name="Account" nillable="true" type="tns:Account" />
</
xs:schema>

As you can see, the XML schema represents nothing more than the types exchanged through the Account entity. It is up to the client (consumer) to associate the representation to an implementation.


I hope this posts helps you to think of your interfaces/classes as contracts and schemas. Having this idealogy for your applications, you can stop worrying about their interactions with remote systems.