Monday, July 05, 2010

Use of Generics in .NET webservices

Recently, one of my team members was trying out an idea of using a generic container "response" object for all return types from the webservice. The advantage of doing so was that we would just have one generic "Response" object instead of many response objects for different operations - hence it would be easier for clients to extract data and consistently get other meta-data information in the Reponse object such as "Success/Error messsage", "Count", etc.
Given below is sample code showing the generic response data contract.
-------------------------------------------


[DataContract]
public class Response
{
[DataMember]
public T Data { get; set; }
[DataMember]
public List DataCollection { get; set; }
[DataMember]
public string ErrorMessage { get; set; }
}

[DataContract]
public class Customer
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember]
public decimal ContactNo { get; set; }
}

[DataContract]
public class Vendor
{
[DataMember]
public string VendorName { get; set; }
[DataMember]
public int Rating { get; set; }
}

-------------------------------------------
As you can see, the actual return object (e.g. vendor, customer) is wrapped in a generic response object. Given below is code snippet of the webservice/WCF service.
-------------------------------------------
public class GenericService : IGenericService
{
public Response DoOtherWork()
{
Response res = new Response();
Vendor c = new Vendor();
c.VendorName = "Rocky"; c.Rating = 3;
res.Data = c;
res.ErrorMessage = "Big Errors";
return res;
}

public Response DoWork(int i)
{
Response res = new Response();
List lstcust = new List();
Customer c = new Customer();
c.Name = "Rocky"; c.Age = 26; c.ContactNo = 7688890909;
int j = 0;
Customer c1 = new Customer();
c1.Name = "dfgdfgRocky"; c1.Age = 26; c1.ContactNo = 7688890909;

lstcust.Add(c); lstcust.Add(c1);
res.DataCollection = lstcust;
res.ErrorMessage = "No Errors";
return res;
}
}

-------------------------------------------
It was interesting to see how the .NET framework handled this in the WSDL. What complex types are created in the WSDL? This was important because the WCF services could potentially be used by Java and Ruby clients.
Here we found a hitch - the WSDL schema had 2 complex types such as "ResponseOfCustomer24335423" and "ResponseOfVendor24r58sdf". This WSDL worked fine when we created stubs in Java by importing the WSDL. But only the random numbers appended at the end of the complex schema was a problem from naming standards and clarity point of view. You would not want your callers to keep wondering why such strange names were given.
But here again, we found an easy workaround. While defining the data-contract, we have the option of specifying the name of the complex type it should appear as in WSDL.
So we just added one key value pair to the Data Contract attribute as follows.
-------------------------------------------
[DataContract(Name = "ResponseOf{0}")]
public class Response
{
[DataMember]
public T Data { get; set; }
[DataMember]
public List DataCollection { get; set; }
[DataMember]
public string ErrorMessage { get; set; }
}

-------------------------------------------
This was cool as could have a placeholder "{0}" for the type passed to the generic container class.
Once we did this, the complex types in the WSDL has user friendly names such as "ResponseOfCustomer" and "ResponseOfVendor".