Monday, June 30, 2008

ExtensibleDataObject and Data Member attributes


So far we have seen simple usage of DataMember’s in DataContract. We have plenty more support for them, let’s look into a few of them

    1. IsRequired – Do clients need to provide value to this field necessarily
    2. Order – The order in which the fields will be applied in SOAP exchange, so that fields can be processed accordingly


Here is a sample of overriding them

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}
[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

Let’s carry this discussion of, “IsRequired” attribute further. If we say IsRequired=false for a field, then we have 2 possibilities from client

    1. Client provides a value – Then service receives the value provided
    2. Client does not provide a value – Default value for the field is applied


In course of time services undergo modifications, some of the possibilities are

    1. Changes to Data members

      a. Change data type of fields – Clients will be affected if new type is not compatible with existing types, else they are compatible
      b. Addition of new data members – Old clients don’t complain, new clients will use new members
      c. Remove DataMembers – If the field removed is a required field then, clients will complain, otherwise they would not. This will lead to an interesting discussion that we pursue next
      d. Add new operations – Same as Addition of new data members
      e. Change signature of operations – If the old type are compatible, then no complaints, otherwise new version has to be drafted
      f. Remove operations – If clients have been using it, then expect them to complain


What will happen if a non-required (IsRequired=false) data member is removed from service, with client unaware of changes. Client keeps pushing that data and how will it be handled at service.

One way to handle this is Derive your DataContract class from IExtensibleDataObject interface. By doing this you have to implement ExtensionData field in your class. But the advantage is, if some old clients update properties/fields that have been chopped, they will get into ExtensionData field (instead of throwing an error), and we may opt to ignore or use it to keep track of number of client still hooked to old service

Here is a sample of it

[DataContract(Namespace = "http://techkrishnan.blogspot.com/2008/06")]
public class HouseContract : IExtensibleDataObject
{
private string owner;
private string tenant;
private DateTime startDate;
private DateTime endDate;
private string description;
private DateTime signedOn;

#region IExtensibleDataObject Members
ExtensionDataObject dataObject;
public ExtensionDataObject ExtensionData
{
get
{
return dataObject;
}
set
{
dataObject = value;
}
}
#endregion

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}

[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

[DataMember(IsRequired = false, Order = 6)]
public string Description
{
get { return description; }
set { description = value; }
}
}

Create a host application and client for this service

After successful execution, comment Description field in service code, without any changes to client or host code. Or may be you may wish to update reference for this dll in service host (that can be done)

Your new DataContract should look something like this

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

namespace ExtensibleDataObjectService
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract(Namespace="http://techkrishnan.blogspot.com/2008/06")]
public interface IExtensibleObjectService
{
[OperationContract(IsInitiating=true)]
void AddContract(HouseContract contract);
// TODO: Add your service operations here
}

// Use a data contract as illustrated in the sample below to add composite types to service operations
[DataContract(Namespace = "http://techkrishnan.blogspot.com/2008/06")]
public class HouseContract : IExtensibleDataObject
{
private string owner;
private string tenant;
private DateTime startDate;
private DateTime endDate;
private string description;
private DateTime signedOn;

#region IExtensibleDataObject Members
ExtensionDataObject dataObject;
public ExtensionDataObject ExtensionData
{
get
{
return dataObject;
}
set
{
dataObject = value;
}
}
#endregion

[DataMember(IsRequired=true, Order=1)]
public String Owner
{
get { return owner; }
set { owner = value; }
}

[DataMember(IsRequired = true, Order=2)]
public string Tenant
{
get { return tenant; }
set { tenant = value; }
}

[DataMember(IsRequired = true, Order=3)]
public DateTime StartDate
{
get { return startDate; }
set { startDate = value; }
}

[DataMember(IsRequired = true,Order=4)]
public DateTime EndDate
{
get { return endDate; }
set { endDate = value; }
}

[DataMember(IsRequired = false, Order=5)]
public DateTime SignedOn
{
get { return signedOn; }
set { signedOn = value; }
}

//[DataMember(IsRequired = false, Order = 6)]
//public string Description
//{
// get { return description; }
// set { description = value; }
//}
}
}

DataMember “Description” has been commented. Now restart host and client in order. Put a break point on the function AddContract in service and you can find description field in Extensiondata. Extension data will contain a dictionary that will have name-value pair. In this case, name will be “Description” and value, as assigned by client

Though it’s not that often we see a property get removed, this possibly shows a way to run on with out any issues with old and new clients


We’ll take a look at more option for OperationContract in the coming days


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; }
}
}

Saturday, June 7, 2008

WCF Exception handling


Exception handling

We could do a simple exception handling from client application. One of the most basic ways of exception handling in the application is given below

try
{
HelloIndigoService.HelloIndigoServiceClient hisc = new HelloIndigoClient.HelloIndigoService.HelloIndigoServiceClient("HelloIndigoNetTcpEndPoint");
Console.WriteLine(hisc.SayHello("Hello Indigo"));
Console.ReadKey(true);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Inner Exception: ");
Console.WriteLine(ex.InnerException.Message);
Console.ReadKey(true);
}

Error handling as in the above code would only give WCF error. If there are any errors within the code, it might not get passed on to the client system.

To implement exception handling service code needs to be changed to as mentioned below

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

namespace HelloIndigo
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract]
public interface IHelloIndigo
{
[OperationContract]
string SayHello(string sName);

[OperationContract]
int GetSquare(int nNum);

[FaultContract(typeof(ConfigFault))]
[OperationContract]
int GetCube(int nNum);

// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations

[DataContract]
public class ConfigFault
{
[DataMember]
public string ConfigOperation;
[DataMember]
public string ConfigReason;
[DataMember]
public string ConfigMessage;
}
}

ConfigFault class is the one that will be populate with appropriate error message and shared with client

WCF Functions cannot throw error messages through ConfigFault unless explicitly told as in the case of the function GetCube in the above code

Let’s see the implantation of GetCube

public int GetCube(int nNum)
{
//handle exception here
if (nNum >= 1 nNum < 1000)
{
ConfigFault cf = new ConfigFault();
cf.ConfigMessage = "Only positive numbers < 1000 are allowed";
cf.ConfigOperation = "GetCube";
cf.ConfigReason = "Number Exception";
throw new FaultException(cf);
}
return (nNum * nNum * nNum);
}

Having seen GetCube function implementation let’s take a look at the client side code changes

try
{
hic.GetCube(-3);
}
catch (FaultException cf)
{
Console.WriteLine(cf.Detail.ConfigMessage);
Console.WriteLine(cf.Detail.ConfigOperation);
Console.WriteLine(cf.Detail.ConfigReason);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}


Obviously this way of handling errors is more informative than normal way.

I’ll see you with more updates

Basic/Easy WCF application

Last time we saw how to do a simple or may be an unprofessional WCF application. In real-time scenarios, you would like to keep you service and host application separately so that host can load as many WCF applications or host remains unaffected by what happens to service. All boil down to Loosely coupled applications and SOA concepts

Let’s create a simple WCF application

Service
Note: If you do not have VS 2008, then create a simple Library, and add reference to System.ServiceModel namespace

HelloIndigoService.cs
namespace HelloIndigoService
{
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config and in the associated .svc file.
public class HelloIndigoService : IHelloIndigoService
{
public string SayHello(string sText)
{
string st = String.Format("Received text {0} at {1}", sText, DateTime.Now);
return st;
}
}
}

IHelloIndigoService.cs
Create a new project and have the name as HelloIndigoService
Add the following code to it

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

namespace HelloIndigoService
{
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.
[ServiceContract]
public interface IHelloIndigoService
{

[OperationContract]
string SayHello(string sText);
// TODO: Add your service operations here
}


// Use a data contract as illustrated in the sample below to add composite types to service operations.
/*[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";

[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}

[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}*/
}

Compile application

Host
We’ll see how to create a simple both Http and TCP host
Create a new Console Application project in the same solution. Name it HelloIndigoHost
Add Reference to HelloIndigoService.dll
Add Reference to System.ServiceModel

Add Application configuration file to the project and do the following.

Open App.Config using WCF Confgiuration tool



  1. Click on Create New Service and choose the dll created in the first project. The name you see should be like “HelloIndigoService.HelloIndigoService”

  2. Accept the default value for the Service Contract window

  3. In the communication mode window, select Http

  4. In the interoperability window, select “Advanced web service interoperablility” and then select “Simplex Communication”

  5. Provide a path of you choice. I chose a name like http://localhost:8000/IndigoService

  6. Click on Finish






Add another endpoint for mexHttpBinding. This is done to allow client query for WSDL information


  1. Expand “Services node” and click on the service you created (“HelloIndigoService.HelloIndigoService). Right-Click on End Point node, and select “New service endpoint”
  2. Name it as “HelloIndigoMexHttpEndoint”
  3. For Address set value as “mex”
  4. Set Binding as “mexHttpBinding”
  5. And contract as “IMetadataExchange”




Once you are done with the steps, then its time for us to construct an endpoint

  1. Name the end point as “HelloIndigoNetTcpEndPoint”
  2. Follow similar step for including a TCP Endpoint, just that address would be slightly different. I used the following net.tcp://localhost:7000/HelloIndigoService


In the configurations window, select the node “Advanced” and pick the node “Service Behaviors”. Add a new Service Behvior with the following configurations

  1. Give it a appropriate name. I called it “serviceBehavior”
  2. Click on the Add button in Configuration Behavior
  3. From the drop down window, select “Service Metadata”
  4. You would see that added to the Service behavior window
  5. Double click on “Service Metadata”. In the General tab set “HttpGetEnabled” property to TRUE
  6. Once you have created a Service Behavior, you need to associate it with the service
  7. Click on the service you created and set “Behvior configuration’ property to “serviceBehavior”





Once you are done with the settings, we have nothing left then to open and close the service host
Open Program.cs and stick the following code

static void Main(string[] args)
{
using(ServiceHost sHost = new ServiceHost(typeof(HelloIndigoService.HelloIndigoService)))
{
sHost.Open();
Console.WriteLine("Started service");
Console.ReadKey(true);
}
}

When ServiceHost is opened. Application looks for the endpoints in App.Config and opens as many as configured. In our case, it would be for http and tcp

You can test to see if these endpoints are working easily. If service host is executed from windows XP or above and the application executes without any error then, the end points are opened. HTTP endpoint can be verified though through IE. Open IE and give the path mentioned in the address section for http binding. In my case it was http://localhost:8000/IndigoService


Client
Now that we have constructed host…we are all set for client construction

If you execute the above link in IE, with mexHttpBinding, you’ll see a line which reads
Svcutil.exe http://localhost:8000?wsdl


  1. Copy that line
  2. Open Visual studio command prompt (You can find it from visual studio tools in start up menu)
  3. Execute the command
  4. This execution would yield to a couple of files. A proxy and config file.
  5. Create a new console application and name it “HelloIndigoClient”. Copy the files proxy and config file generated in the previous step
  6. Paste it in you client application.
  7. Rename the file to App.config

Add the following code

HelloIndigoService.HelloIndigoServiceClient hisc = new HelloIndigoClient.HelloIndigoService.HelloIndigoServiceClient("HelloIndigoNetTcpEndPoint");
Console.WriteLine(hisc.SayHello("Hello Indigo"));
Console.ReadKey(true);

With this the basic model for a professional webservice is ready. Now you can add DB connections, error handling and other logics to web service. This will have no impact on service host and client application. If there are any changes done to web service, generate proxy for web service and use the new files from client application.

Going forward, we'll create more webservice solutions to handle error conditions