Sunday 7 November 2010

Combing Code Contracts with DataContracts for better contract expressiveness and quality

I'm going to write some more about Code Contracts (http://research.microsoft.com/en-us/projects/contracts/) soon, but wanted to show you a neat example of using in combination with DataContracts.

This shows the power of CodeContracts in enforcing more semantic information about a DataContract than is currently expressible in WSDL or DataContracts.

For example take a look at this DataContract:

   [DataContract]
   public class Address
   {
       [DataMember(IsRequired=false)]
       public int? HouseNumber { get; set; }
       [DataMember(IsRequired = false)]
       public string HouseName { get; set; }
       [DataMember(IsRequired = true)]
       public string Street { get; set; }
       [DataMember(IsRequired = true)]
       public string City { get; set; }
       [DataMember(IsRequired = true)]
       public stringRegion { get; set; }
       [DataMember(IsRequired = true)]
       public string Country { get; set; }
       [DataMember(IsRequired = false)]
       public string PostCode { get; set; }
   }

Then we can see that there are several attributes which indicate that they are optional, for example the House Number and House Name. In the UK then many houses will have either one or the other or both. However no address should be missing both, the problem with this contract is that this isn't clear or enforceable.

If we enhance this DataContract with CodeContracts:

   [DataContract]
   public class Address
   {
       [DataMember(IsRequired=false)]
       public int? HouseNumber { get; set; }
       [DataMember(IsRequired = false)]
       public string HouseName { get; set; }
       [DataMember(IsRequired = true)]
       public string Street { get; set; }
       [DataMember(IsRequired = true)]
       public string City { get; set; }
       [DataMember(IsRequired = true)]
       public stringRegion { get; set; }
       [DataMember(IsRequired = true)]
       public string Country { get; set; }
       [DataMember(IsRequired = false)]
       public string PostCode { get; set; }
       [ContractInvariantMethod]
       private void EnforceInvariant()
       {
           Contract.Invariant(HouseNumber > 0);
           Contract.Invariant(!(HouseNumber == null && string.IsNullOrWhiteSpace(HouseName)));
           Contract.Invariant(!string.IsNullOrWhiteSpace(Street));
           Contract.Invariant(!string.IsNullOrWhiteSpace(City));
           Contract.Invariant(!string.IsNullOrWhiteSpace(Region));
           Contract.Invariant(!string.IsNullOrWhiteSpace(Country));
       }
   }

Here you can see I have added a method that is decorated with a ContractInvariantMethod attribute. When you run the code through the Code Contract post compiler then this will ensure that at each point that the class changes that these invariants remain true. I've also added some invariants that ensure that the values are not null, empty or whitespace strings and that the house number is greater than 0. This can be analysed with a static analysis tool, with PEX and CHESS tools for testing and runtime checks.

When I am working with WSDL produced from .NET WCF services then I like to get the DataContract assemblies so that these checks are possible. It is also better to go to the source rather than generating these from the WSDL - as long as the people that wrote the contracts have done the decent thing and put their DataContracts in a separate assembly to isolate it from the other layers and behaviours.

What do you think of this approach - do you think it enhances the data contract? Leave some comments below.

No comments:

Post a Comment