Adding client-side helpers to service-defined contracts

Summary: When a client adds a service reference to a service, the generated classes are marked as partial, which lets the client add additional helper methods and local functionality to the classes.

 

When a client generates a contract from a service, only the data and method signatures are included (and not the method implementation). The constructor is not part of the contract either. Here’s an example:

 

The service, running in Windows Azure, defines a LatLongLocation contract. It looks like

 

    [DataContract]

    public class LatLongLocation

    {

        public LatLongLocation(double latitude, double longitude)

        {

            this.Latitude = latitude;

            this.Longitude = longitude;

        }

        [DataMember]

        public double Latitude { get; set; }

        [DataMember]

        public double Longitude { get; set; }

    }

 

The contract (as defined by the service) has a constructor to make it easy (for the service) to construct this object, however this constructor is never sent over the wire. When a client consumes this contract (by adding a reference to the service), only the data members (and method signatures) are generated (see below). Pay special attention to the fact that this is a partial class, which will be useful later on for the client.

 

    [System.Runtime.Serialization.DataContractAttribute(Name="LatLongLocation", Namespace="https://schemas.datacontract.org/2004/07/PresenceService")]

    [System.SerializableAttribute()]

    public partial class LatLongLocation : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

       

        [System.Runtime.Serialization.OptionalFieldAttribute()]

        private double LatitudeField;

       

        [System.Runtime.Serialization.OptionalFieldAttribute()]

        private double LongitudeField;

       

        [System.Runtime.Serialization.DataMemberAttribute()]

        public double Latitude {

            get {

                return this.LatitudeField;

            }

            set {

                if ((this.LatitudeField.Equals(value) != true)) {

                    this.LatitudeField = value;

                    this.RaisePropertyChanged("Latitude");

                }

            }

        }

       

        [System.Runtime.Serialization.DataMemberAttribute()]

        public double Longitude {

            get {

                return this.LongitudeField;

            }

            set {

                if ((this.LongitudeField.Equals(value) != true)) {

                    this.LongitudeField = value;

                    this.RaisePropertyChanged("Longitude");

                }

            }

        }

    }

 

The client wants to consume the LatLongLocation contract. The client is a program running on a laptop with a GPS sensor. It can use the Windows 7 location platform to get the current location and then send it to the Azure service. The location platform returns data as a  LatLongLocationReport and this needs to be converted into a LatLongLocation so the service can consume it.

 

The client program has added a reference to the Azure service. The service metadata is downloaded when adding a reference using Visual Studio and the contract is generated from it. To the client code, the following partial class is added as a convenience to construct LatLongLocation objects.

 

    // The LatLongLocation type is defined by the Presence Service, but

    // the generated reference to the service only contains data members (by design).

    // This adds a new type of constructor to use with data from

    // the Win7 location platform.

    public partial class LatLongLocation

    {

        public LatLongLocation(Windows7.Location.LatLongLocationReport report)

        {

            this.Latitude = report.Latitude;

            this.Longitude = report.Longitude;

        }

        public LatLongLocation(double latitude, double longitude)

        {

            this.Latitude = latitude;

            this.Longitude = longitude;

        }

    }

 

(Note that the LatLongLocation (latitude, longitude) constructor is duplicated from the service definition out of necessity and is only added as a convenience for the client-side code. In fact the client may not even be aware that the same constructor ever existed on the service-side).

 

This shows a client can add additional local functionality (LatLongLocationReport) to the service-side defined contract (LatLongLocation). The functionality added on the client side won’t be sent over the wire and affect the service either. In addition, if the generated contract ever needs to be regenerated, it won’t wipe out the additional code added on the client-side.