Monday, November 26, 2012

WCF REST with multiple DataContract parameters

When you are creating a RESTful WCF service that requires multiple DataContract parameters, there are a couple of different options for handling this requirement and staying true to a RESTful uri implementation. I think that each are valid and really depend more upon your view of the experience you want to provide the consumers and / or integrators of your SOA.

Wrapped Body 
By decorating the method within the service interface with the wrapped body attribute, this informs the method that all parameters will be wrapped within a single object during transport.  This allows you to keep your DataContracts separated and singularly implemented, while allowing for execution of complex methods.  Here is an example of using the wrapped request attribute.

[DataContract]
public class Label
{
   [DataMember]
   public Int64 ID { get; set; }      
   [DataMember]
   public String Name { get; set; }        
}

[DataContract]
public class Album
{
   [DataMember]
   public Int64 ID { get; set; }
   [DataMember]
   public String Artist { get; set; }
   [DataMember]
   public String Name { get; set; }
   [DataMember]
   public String Genre { get; set; }
}

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/AlbumAndLabel",
    BodyStyle = WebMessageBodyStyle.WrappedRequest),
    Description("Adds a new album and label to the corresponding list")]
void AddAlbumAndLabel(Album album, Label label);

This will require the consumer of your service to serialize both of the contracts into a container object as part of the message interaction with the service.  So, the integrator would have to manually create and post a message body that resembled this:

<AddAlbumAndLabel>
   <Album>
      <ID></ID>
      <Artist>Nine Lashes</Artist>
      <Name>World We View</Name>
      <Genre>Hard Rock</Genre>
   </Album>
   <Label>
       <ID></ID>
       <Name>Tooth And Nail</Name>
   </Label>
</AddAlbumAndLabel>

This is entirely effective and will result in the ability to have multiple parameters.  This will give you the ability as well to migrate all functionality away from QueryString parameters and into a single wrapped message body.  The downside to this method is that your consuming developers will have to manually serialize multiple objects into a non-existent wrapper object.  The upside is that you will have less DataContracts to manage and maintain.

Method-Specific Complex Types
While less dynamic and more manual on the service side, this give you the ability to abstract the 'difficulty' in serializing multiple types into a wrapped body away from the user of your services.  In this model, you would create a new DataContract specific to your complex method.  Basically, you are taking the step of creating a wrapper object for transport on the service side. This approach will force you to design a user story focused service wherein each detailed user story has a single associated service method.  In this case, a new DataContract would be created as follows:

[DataContract]
public class AlbumAndLabel

{
   [DataMember]
   public Label Label{ get; set; }      
   [DataMember]
   public Album Album { get; set; }        
}

[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/AlbumAndLabel"),
    Description("Adds a new album and label to the corresponding list")]
void AddAlbumAndLabel(AlbumAndLabel albumAndLabel);

This will allow you to expose a library of user story focused DataContracts and methods to the consumers of your services, allowing them to more easily create complex types to be passed to your methods with a single serialization routine.  The downside to this method is that you have a list of DataContracts that are only needed for a single method.  The upside to this method, is that your consuming developers can leverage your service with a lesser knowledge level.

1 comment: