Web service contracts and AutoMapper

 

I’ve recently had a great experience using AutoMapper on a project (the project is iMeta’s solution to managing information used to settle trades, Assassin).

I used it to map from my service contract back into the domain model of our application.

There’s a bunch of really good reasons (e.g. ease of use and protecting people from change) why a service contract is not the same as your internal data model.  There are also technical reasons, for example, you don’t want to litter your internal data model with WCF attributes.  So you end up having a bunch of classes that represent your service contract that are similar to but not the same as your internal data model.  Therefore, there is overhead maintaining the mappings between those two object models – this is where AutoMapper helps.  AutoMapper also helped reduce the amount of code we had to write to start off with, but that is a one off cost and doesn’t really take that long to write (even though after 30 mins of a.x = b.y does make you want to pull out your eyes).  The biggest win is maintenance by far.

If you’re in control of your contract / object model it is a good idea to make life as easy for yourself as possible.  For example, try and make property names match – hopefully they naturally will. That way AutoMapper will map, by convention, most of your properties automatically.  80% of what you need for 20% of the effort!

You can download AutoMapper via Horn, which I find the easiest way to get the latest version of open source binaries!

To help illustrate what I’m talking about I’ll finish with an example.  I haven’t shown the projects usage (it wouldn’t make sense unless you’d like to first understand trade settlement data) but here’s an example (contrived a bit):

   1: // setup the mapper
   2: Mapper.CreateMap<ServiceContract.Customer, Domain.Customer>()
   3:    .ForMember(c => c.Products, opt => opt.MapFrom(sc => sc.InterestedIn));
   4: Mapper.CreateMap<ServiceContract.Product, Domain.Product>()
   5:    .AfterMap((contractProduct, domainProduct)
   6:              => domainProduct.ProductType = LookupProductType(contractProduct.Name));
   7: Mapper.CreateMap<ServiceContract.Address, Domain.Address>();
   8:  
   9: // setup some data
  10: var serviceCustomer = new ServiceContract.Customer
  11:                          {
  12:                             Firstname = "John",
  13:                             Surname = "Smith",
  14:                             DateOfBirth = new DateTime(1980, 10, 10),
  15:                             Address = new ServiceContract.Address {HouseName = "1", Postcode = "postcode"}
  16:                          };
  17: serviceCustomer.InterestedIn.Add(new ServiceContract.Product{Name = "Something for the wife"});
  18:  
  19: // map it
  20: var domainCustomer = new Domain.Customer();
  21: Mapper.Map(serviceCustomer, domainCustomer);
  22:  
  23: private static ProductType LookupProductType(string name)
  24: {
  25:    // do some db lookup maybe
  26: }

As you can see AutoMapper can also update an instance object, so if someone wanted to provide updates to existing objects then it’s a piece of cake too!

Here are the classes which are being mapped:

   1: namespace Domain
   2: {
   3:  
   4:    public class Customer
   5:    {
   6:       public Customer()
   7:       {
   8:          Products = new ProductCollection();
   9:          ContactHistory = new ContactHistory();
  10:       }
  11:  
  12:       public string Firstname { get; set; }
  13:       public string Surname { get; set; }
  14:       public DateTime DateOfBirth { get; set; }
  15:       public Address Address { get; set; }
  16:       public string EmailAddress { get; set;}
  17:  
  18:       public ProductCollection Products { get; private set; }
  19:  
  20:       // extra stuff on customer that you don't want third parties to provide
  21:       public ContactHistory ContactHistory { get; private set; }
  22:       // .....
  23:    }
  24:    
  25:    public class ContactHistory
  26:    {
  27:    }
  28:  
  29:    public class Address
  30:    {
  31:       public string HouseName {get; set;}
  32:       public string Postcode { get; set; }
  33:    }
  34:  
  35:    
  36:    public class ProductCollection : List<Product>
  37:    { }
  38:  
  39:    public class Product
  40:    {
  41:       public ProductType ProductType { get; set; }
  42:    }
  43:  
  44:    public class ProductType
  45:    {
  46:       public string Name { get; set; }
  47:    }
  48: }
  49:  
  50: namespace ServiceContract
  51: {
  52:    [DataContract]
  53:    public class Customer
  54:    {
  55:       public Customer()
  56:       {
  57:          InterestedIn = new List<Product>();
  58:       }
  59:  
  60:       [DataMember]
  61:       public string Firstname { get; set; }
  62:       [DataMember]
  63:       public string Surname { get; set; }
  64:       [DataMember]
  65:       public DateTime DateOfBirth { get; set; }
  66:       [DataMember]
  67:       public Address Address { get; set; }
  68:       [DataMember]
  69:       public string EmailAddress { get; set; }
  70:       [DataMember]
  71:       public List<Product> InterestedIn { get; set; }
  72:    }
  73:  
  74:    [DataContract]
  75:    public class Product
  76:    {
  77:       [DataMember]
  78:       public string Name { get; set; }
  79:    }
  80:  
  81:    [DataContract]
  82:    public class Address
  83:    {
  84:       [DataMember]
  85:       public string HouseName { get; set; }
  86:       [DataMember]
  87:       public string Postcode { get; set; }
  88:    }
  89: }

Some more examples can be found on the codeplex site:  http://www.codeplex.com/AutoMapper

Comments

No comments posted yet.

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 2 and 3 and type the answer here:

Preview Your Comment.