Saturday, June 28, 2008

Data and Message Contracts


Data Contract
Last edition was more of an introduction to a professional web service creation. Let’s take a deeper dive this week

We will take a look at how to work with custom data types with clients. It’s not always that we want web service to expose just basic data type. For e.g. say we wish to create a web service that accepts links and store them in DB for future reference.

To make things simple one would have opted to create a function with signature mentioned below.
void SaveLink(string id, string name, string description, dateTime startDate, DateTime endDate, string url);

Alternatively, we can write the same as
void SaveLink(LinkItem item);

First clients should be aware of LinkItem data type to use this service. And that’s when DataContract is introduced

Let’s create a simple WCF Class Library project – “DataContractService”

IContentManagerService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace DataContractService
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract(Name="ContentManagerContract", Namespace="http://techkrishnan.bloggerspot.com/wcf/2008/06")]
public interface IContentManagerService
{
[OperationContract]
void SaveLink(LinkItem item);
[OperationContract]
LinkItem GetLink();

}

// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract]
public class LinkItem
{
private string m_id;
[DataMember]
public string Id
{
get { return m_id; }
set { m_id = value; }
}

private string m_title;
[DataMember]
public string Title
{
get { return m_title; }
set { m_title = value; }
}

private string m_description;
[DataMember]
public string Description
{
get { return m_description; }
set { m_description = value; }
}

private DateTime m_dateStart;
[DataMember]
public DateTime DateStart
{
get { return m_dateStart; }
set { m_dateStart = value; }
}

private DateTime m_dateEnd;
[DataMember]
public DateTime DateEnd
{
get { return m_dateEnd; }
set { m_dateEnd = value; }
}

private string m_url;
[DataMember]
public string Url
{
get { return m_url; }
set { m_url = value; }
}
}
}


ContentManagerService.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace DataContractService
{
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in App.config.
public class ContentManagerService : IContentManagerService
{
private LinkItem m_LinkItem;

public void SaveLink(LinkItem item)
{
m_LinkItem = item;
}

public LinkItem GetLink()
{
return m_LinkItem;
}
}
}

Host Application
Once we have a basic web service, let’s launch a host application for this. Host application is no different from what we had done in the previous exercise

Let explain you that
1. Create a simple Project with in the same solution – “DataContractServiceHost”
2. Add reference to ServiceModel assembly and DataContractService dll
3. Create a new App.Config file and open it with WCF configuration editor.
4. Add a new Service Endpoint with the following information
a. Provide contract information
b. Set protocol to be http
c. Relative address
d. Name for service endpoint
5. Add a similar one for TCP and mexHttpBinding, as mentioned in previous exercise
6. Add new Host for Http endpoints
7. Add new service behavior for Http and Tcp. Ensure “GetHttpEnabled” property for Http is set to true
8. Associate ServiceBehavior to the service
9. Open Program.cs and load code to open ServiceHost and wait of people to access service

Client application
1. Open the service in web browser. I had path of http://localhost:8080/DataContractService and net.tct://localhost:8000/DataContractService for http and Tcp protocol
2. Ensure service is running. Open VisualStudio command prompt and execute svcutil http://localhost:8080/
3. This would result in 2 files. In my case it was ContentManagerService.cs and output.config
4. Create a new console application in DataContractService solution – DataContractServiceClient
5. Add file generated in step 4 into this project
6. Rename output.config to app.config
7. Add reference to ServiceModel library and System.Runtime library
8. Open Program.cs and put code to invoke SaveLink and GetLink functions
9. Sample code is appended

ContentManagerContractClient client = new ContentManagerContractClient("ContentManagerServiceHttpEndPoint");
DataContractService.LinkItem link = new DataContractService.LinkItem();
link.Id = "10";
link.Title = "Adi Sankara's works";
link.Description = "All works of Adi sankara can be found in this link";
link.DateStart = DateTime.Now;
link.DateEnd = DateTime.Now;
link.Url = "http://res0.esnips.com/doc/38f7e33b-87fd-428c-a0da-bdc8ca1eeb37/Nirvana-Shatkam";
client.SaveLink(link);

Make a call to GetLink in similar fashion

KnownType
Now lets bring in some design pattern. If we want to differentiate different link types, say
1. MP3Link
2. PhotoLink
3. GigLink

All these achieve the same objective of LinkType but just specialization of its kind.

Here’s the hierarchy of what we discussed

[DataContract(Namespace="http://techkrishnan.bloggerspot.com/wcf/2008/06")]
public class Mp3Link : LinkItem
{
//add specialization code here
public Mp3Link()
{
}
}

[DataContract(Namespace = "http://techkrishnan.bloggerspot.com/wcf/2008/06")]
public class PhotoLink : LinkItem
{
//add specialization code here
public PhotoLink()
{
}
}

[DataContract(Namespace = "http://techkrishnan.bloggerspot.com/wcf/2008/06")]
public class GigLink: LinkItem
{
//add specialization code here
public GigLink()
{
}
}

We have a single function that accepts LinkItem as parameter
void SaveLink(LinkItem item);

But we are not going to pass LinkItem, we’d going to have derivatives of LinkItem. Moreover derivatives list can keep growing with time.

To accommodate these dign changes, we need to make just a small change in our code

[DataContract(Namespace = "http://techkrishnan.bloggerspot.com/wcf/2008/06")]
[KnownType(typeof(Mp3Link))]
[KnownType(typeof(PhotoLink))]
[KnownType(typeof(GigLink))]
public class LinkItem
{
}

This would ensure we can pass derivatives of LinkItem. Now specializations can grow to any level, we just add them here and proceed.

Isn’t that easy. For people who would not like to touch any code level changes for this, we can do this through app.config too. I’ll share them on my later section

There are more ways to do similar option, we’ll do them in a little while from now

MessageContract
This way of development does not give any control on SOAP message genertaed.

As you would be aware of SOAP protocol, it has different section, envelop, header and body. DataContract does not exercise any control on them. If you’d like to have any control on them, use “MessageContract”. This allows you specify
1. Members as header
2. Members as body members
3. Protection level – to encrypt/sign

How do we achieve all this. They are very simple to do. Here is a sample piece of the same

[MessageContract]
public class CategoryAttribute
{
private string id;
private DateTime modifiedDate;
private string name;
private string description;

[MessageHeader]
public string ID
{
get { return id; }
set { id = value; }
}

[MessageHeader]
public DateTime ModifiedDate
{
get { return modifiedDate; }
set { modifiedDate = value; }
}


[MessageBodyMember(Order=1)]
public string Name
{
get { return name; }
set { name = id; }
}


[MessageBodyMember(Order=2)]
public string Description
{
get { return description; }
set { description = value; }
}
}

No comments: