WCF Services—Message Contracts 2

Jump to: navigation, search
Visual C# Tutorials
.NET Framework Tutorials

WCF Services

© 2007 Chris Peiris, Dennis Mulder

Message Contracts

In this section, we’ll present a quick example of message contracts related to QuickReturns Ltd. Remember, three fields are required to be set to predefined values in the SOAP header. Why would you mandate headers in the SOAP request? One scenario that is common is for applying policies and rules based upon the content of the SOAP request. If you can promote or present some attribute of the request to the SOAP header, it’s easily validated before any downstream code processes the request in your service implementation.

If you take a look at the Trade class in Listing 4-25 (part of the accompanying code in Example09), you can see that it has been updated with a specific namespace in addition to being decorated with the DataMember attribute with a mix of fields and properties. Additionally, the Execution class, shown in Listing 4-26, has been similarly decorated.

Listing 4-25. Trade Data Contract (Partial)

namespace ExchangeService
{
    [DataContract(
        Namespace = "http://PracticalWcf/Exchange/Trade" )]
    public class Trade
    {
        string _ticker;
        char _type;
        string _publisher;
 
        [DataMember(
            Name = "Participant", IsRequired = true, Order = 0 )]
        string _participant;
 
        [DataMember(
            Name = "QuotedPrice", IsRequired = false, Order = 1 )]
        internal double _quotedPrice;
 
        [DataMember(
            Name = "Quantity", IsRequired = true, Order = 1 )]
        private int _quantity;
 
        [DataMember(
            Name = "TradeTime", IsRequired = false, Order = 9 )]
        Nullable<DateTime> _tradeTime;
 
        double _executionAmount;
 
        /// <summary>
        /// Primary exchange security identifier
        /// </summary>
        [DataMember( IsRequired = true, Order = 3 )]
        public string Ticker
        {
            get { return _ticker; }
            set { _ticker = value; }
        }

Listing 4-26. Execution Data Contract (Partial)

namespace ExchangeService
{
    [DataContract(
        Namespace = "http://PracticalWcf/Exchange/Execution" )]
    public class Execution
    {
        [DataMember(Name= "SettleDate")]
        DateTime _settlementDate;
 
        [DataMember( Name = "Participant" )]
        string _participant;
 
        [DataMember( Name = "ExecutionAmount" )]
        double _executionAmount;
 
        [DataMember( Name = "TradeSubmitted" )]
        Trade _trade;

Message contracts allow the encapsulation of data contracts in addition to specifying what part of the message is in the message header and message body. So, for this example, we’ve added a single source code file that contains the definition of two additional classes: TradeSecurityRequest and TradeSecurityResponse. These classes are then decorated as required with the MessageContract attribute. Additionally, the members are then decorated with either the MessageHeader attribute or the MessageBody attribute, as shown in Listing 4-27.

Listing 4-27. Messages—TradeSecurityRequest (Partial)

[MessageContract]
public class TradeSecurityRequest
{
    Trade _trade;
    string _particpant;
    string _publisher;
    string _ticker;
 
    [MessageHeader(MustUnderstand=true)]
    public string Participant
    {
        get
        {
            return _particpant;
        }
        set
        {
            _particpant = value;
        }
    }
 
    [MessageBody]
    public Trade TradeItem
    {
        get
        {
            return _trade;
        }
        set
        {
            _trade = value;
        }
    }

Looking at the MessageHeader attribute on Participant, the MustUnderstand property transfers the responsibility of enforcing this header on the SOAP request to the WCF framework. So, with a simple attribute and property value, we’ve now provided a simple validation. Listing 4-28 illustrates how to use the MessageContract and MessageBody attributes as applied to the TradeSecurityResponse message in this example.

Listing 4-28. TradeSecurityResponse Message

[MessageContract]
public class TradeSecurityResponse
{
    [MessageBody]
    public Execution ExecutionReport;
}

The response message is simply an encapsulation of the execution data contract. We’ve simply encapsulated the data contracts and promoted certain fields or properties as header values.

If you take a look at the update TradeService implementation shown in Listing 4-29, you’ll see several changes.

Listing 4-29. Updated TradeService (Partial)

[ServiceContract(
    Namespace = "http://PracticalWcf/Exchange",
    Name = "TradeService"
    )
]
public interface ITradeService
{
    [OperationContract(
    Action = "http://PracticalWcf/Exchange/TradeService/TradeSecurityAtMarket"
    )]
    [FaultContract( typeof( ArgumentException ) )]
    TradeSecurityResponse TradeSecurity( TradeSecurityRequest tradeRequest );
}

The first change is the explicit specification of the Action property of OperationContract. The second is the addition of the FaultContract attribute to the TradeSecurity method. And finally, the TradeSecurity interface itself, as defined in ITradeService, has been updated to take the respective message contracts from the classes defined in Messages.cs.

Specifically, the first change, the addition of Action, is for illustrative purposes only to show how you can control these values. The WCF framework would provide default WS-Addressing and SOAP headers as required based upon the ServiceContract namespace, name, and operation name.

The second change is the FaultContract attribute. So far, all the examples have had limited exception processing. However, it’s important to note that .NET exceptions and SOAP exceptions are different. Therefore, the FaultContract capability of WCF provides a way to map, encapsulate, and override how faults are handled and reported. This is important because given the cross-platform capability of WCF, it would not be feasible to enforce knowledge of .NET types. Therefore, in this example, we’ve wrapped the TradeService implementation in a try...catch and provided a throw of FaultException in the catch block as follows:

throw new FaultException<ArgumentException>( ex );

The final change is the modification to the TradeSecurity operation. The signature has been updated to receive and respond with the corresponding TradeSecurityRequest and TradeSecurityResponse messages, respectively.

With the service contract change, the TradeSecurity implementation changes to match the interface signature. You now have direct and simple property-level access to the SOAP headers that the client of the service contract must present. Although we are using in our examples WCF-generated proxies and .NET clients, this requirement is a SOAP standard and regardless of the implementation technology—Java, C++, or some other SOAP framework—as long as they implement the specifications, you have enforcement of your MustUnderstand rule.

In Listing 4-30, we’ve provided the simple value validation of the three headers presented using .NET properties from the message contract.

Listing 4-30. TradeService Header Check Implementation (Partial)

public class TradeService : ITradeService
{
    const double IBM_Price = 80.50D;
    const double MSFT_Price = 30.25D;
    public TradeSecurityResponse TradeSecurity( TradeSecurityRequest trade )
    {
        try
        {
            //Embedded rules
            if( trade.Participant != "ABC" )
                throw new ArgumentException( "Particpant must be \"ABC\"" );
 
            if( trade.Publisher != "XYZ" )
                throw new ArgumentException( "Publisher must be \"XYZ\"" );
 
            if( trade.Ticker != "MSFT" )
                throw new ArgumentException( "Ticker must be \"MSFT\"" );

The completed solution provides for client-side trapping of the fault exceptions leveraging the WCF capabilities. On the client side, using WCF, apply the FaultException generic with the ArgumentException type to trap and process as required by the fault condition, as shown in Listing 4-31.

Listing 4-31. Client program.cs Catch Block on Fault Exception (Partial)

catch( FaultException<ArgumentException> ex )
{
     Console.WriteLine( "ArgumentException Occurred" );
     Console.WriteLine( "\tAction:\t" + ex.Action );
     Console.WriteLine( "\tName:\t" + ex.Code.Name );
     Console.WriteLine( "\tMessage:\t" + ex.Detail.Message );
}

The FaultException type provides access to the SOAP fault headers through simple properties, allowing exception handling or reporting as needed.

The service contract side of the channel is extensible by providing your own implementation of the IErrorHandler interface. This interface, when extended, is added to your own service contract implementations, or you can add it to the DispatchBehavior.ErrorHandlers collection, which can provide overriding how messages are transformed into objects and dispatched to methods.


prevpp.png  nextpp.png
C# Online.NET