Introduction
This article describes the process of a WCF service configuration for working with data in JSON format. Although the configuring process seems to be simple,
there are some particularities which cannot be ignored.
Background
As the lead developer in a multinational software company, I often develop Rich Internet Applications and do care about ensuring that an app would have a long and happy life.
And lately, I have a really heavy headache when it comes to the decision about using the proper technology. From one side, the two most used ones are Silverlight and Flash.
Both of them are cross-platform technologies, so an application created on Silverlight or Flash will look the same in all browsers.
From the other side, IMHO, these technologies have one serious disadvantage: they are limited in use. You can’t use a Silverlight application on Linux or a Flash
application on iOS. I didn’t even mention mobile systems that are an essential part of our life. The majority of modern mobile devices and tablets don’t provide
full support for Silverlight and Flash.
The use of HTML and JavaScript allows eliminating compatibility issues. All that’s needed to work with such applications is a web browser on any device.
I won’t bother you further with reasoning pros and cons of different technologies – I use them all, and the decision often depends on a particular project. But today I would like
to tell you what made up my mind to choose HTML and JavaScript rather than other technologies for one of the projects I have been working on.
Creating a WCF service
In one of our projects, I decided to use HTML+JavaScript binding as a client part of an application. The client application interacts with a server to get data.
It is possible to use a WCF service, MVC framework controllers, and Web Handlers as a service. In our case, we need to use a general format for data with which
the client’s application and the service will interact. So, we had to choose what to use: XML or JSON. After some tests and considerations, we stooped on JSON.
We had several reasons which determined the use of JSON.
Now, I want to tell you how we created the WCF service which passes data in JSON format.
The first step is to add a WCF service to the website.
Select the WCF Service in the Add New Item dialog window (I add the service with the WcfJsonService name). After this, the WcfJsonService.svc file is added
to the application along with two files: IWcfJsonService.cs and WcfJsonService.svc.cs.
Let me go a little bit deeper into these files added to the project. The IWcfJsonService.cs file defines the interface implemented by a service. It contains
attributes which specify to WCF how to use the service. The implementation of the service is described in the code-behind file (WcfJsonService.svc.cs).
This file is added to the .svc file. You can use both these files, but I prefer removing them and creating a library of classes instead. This library contains
interface and service implementations. As a result, we come to a more convenient way to manage the code of our WCF services, because of:
- flexibility when adding other providers which implement the service interface;
- easy creation of unit tests.
Further, you will see that the approach doesn’t complicate development, it only requires adding the reference to the library with the class.
Changing the Service Declaration File
We need to change the .svc file to implement our service correctly. Here is an example of a directive which is created in the .svc file by default:
<%@ ServiceHost Language="C#" Debug="true" Service="WebApplication.WcfJsonService"
CodeBehind="WcfJsonService.svc.cs" %>
And below, you can see a new version of the directive. This directive indicates which class is used for the service implementation.
The WebScriptServiceHostFactory
(http://msdn.microsoft.com/en-us/library/bb336135.aspx) class
is indicated for the factory. This class allows the use of web protocols and adds the JSON serialization which is needed for the work of the service with jQuery AJAX:
<%@ ServiceHost Language="C#" Debug="true"
Service="WCFImplementation.ContinentsPopulation"
Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"%>
Creating the Service Interface
The next step is to add a class for the service implementation. The first file I add is a service interface. In my example, I display a list of continents
with population size. And I name this service interface IContinentsPopulation.cs. Now it is necessary to define a contract for the service. I mark my interface with the
ServiceContract
attribute. The WCF attributes are located in the System.ServiceModel
namespace and I need to add a reference to it.
[ServiceContract]
public interface IContinentsPopulation
Now I can add method signatures to the interface. I add two methods for the application:
GetContinents
which gets a list of continents with population;GetContinentDetails
which returns data for the continent with specified name.
Below you can see the method descriptions:
[OperationContract(Name = "GetContinents")]
ContinentsListResponse GetContinents();
[OperationContract(Name = "GetContinentDetails")]
ContinentDetailsRespnse GetContinentDetails(ContinentDetailRequest request);
Implementing the Service
I can add a class for the service implementation. I create the ContinentsPopulation.cs file. The ContinentsPopulation
class implements
the IContinentsPopulation
interface. It is described with the ServiceBehavior
attribute. In my example, the
IncludeExceptionDetailInFaults
property is set to true
. I did this on purpose in order that all the messages about
exceptions are sent to the client and serialized in the required form. In my case, jQuery gets the JSON data.
Now, I should have references to the classes which don’t exist. They are ContinentsListResponse
, ContinentDetailsResponse
, and
ContinentDetailRequest
. But first I need to create them. Each of these classes is a message class. The ContinentsListResponse
and
ContinentDetailsResponse
classes are the message classes for messages between the server and the client. To be more specific, they are classes which include data about
responses from the server. The ContinentDetailRequest
class is a message class from a client to a server, that is the class that includes data about responses to the server.
Now let’s create a separate library of classes. This library allows separating the data model from the service implementation. I add my classes to it.
Besides, I can use the message classes in other projects when implementing a client part.
Message classes are serialized using WCF. That’s why I need to add the DataContact
attribute to the class and the DataMember
attribute to each member
of the class. A member name is defined in this attribute. Then I need to specify that this value is obligatory: IsRequired=true
.
And finally, I need to indicate the order of data positioning: Order = 0. This means that the value will be the first or with a 0 index in a serialized object.
The DataContact
and DataMember
attributes are in the System.Runtime.Serialization
namespace, and I need to add a reference to it.
The message classes are shown below. They contain the continent name and population size values:
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentPopulation")]
public class ContinentPopulation
{
[DataMember(Name = "ContinentName",IsRequired = true,Order = 0)]
public string ContinentName { get; set; }
[DataMember(Name = "TotalPopulation", IsRequired = true, Order = 1)]
public int TotalPopulation { get; set; }
}
[/cc]
It contains the detailed data about the continent: a continent name, area, percentage of the total land area, value of population size, and percentage to the total population of the Earth.
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetails")]
public class ContinentDetails
{
[DataMember(Name = "ContinentName", IsRequired = true, Order = 0)]
public string ContinentName { get; set; }
[DataMember(Name = "Area", IsRequired = true, Order = 1)]
public long Area { get; set; }
[DataMember(Name = "PercentOfTotalLandmass", IsRequired = true, Order = 2)]
public long PercentOfTotalLandmass { get; set; }
[DataMember(Name = "TotalPopulation", IsRequired = true, Order = 3)]
public long TotalPopulation { get; set; }
[DataMember(Name = "PercentOfTotalPopulation", IsRequired = true, Order = 4)]
public long PercentOfTotalPopulation { get; set; }
}[/cc]
It contains the continent name for which the detailed data should be obtained:
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetailRequest")]
public class ContinentDetailRequest
{
[DataMember(Name = "ContinentName", IsRequired = true, Order = 0)]
public string ContinentName { get; set; }
}
[/cc]
It is a class that represents the response from the server with detailed data about the continent.
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentDetailsResponse")]
public class ContinentDetailsResponse
{
[DataMember(Name = "Details", IsRequired = true, Order = 0)]
public ContinentDetails Details { get; set; }
}
[/cc]
It is a class that represents the response from the server with a list of continents and population size.
[cc lang="Csharp" escaped="true"][DataContract(Name = "ContinentsListResponse")]
public class ContinentsListResponse
{
[DataMember(Name = "Continents", IsRequired = true, Order = 0)]
public List Continents { get; set; }
}[/cc]
In real projects, the data which the server gets is taken from a database. I won’t complicate my example with creation of classes for connection to a database
and fill data from the code. I create the Continents
private field in the class. This field is initialized when a service instance is created and is filled
in the InitializeData
method.
The implementation of the ContinentsPopulation
class is represented below:
[cc lang="Csharp" escaped="true"][ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ContinentsPopulation : IContinentsPopulation
{
private List continents = null;
public ContinentsPopulation()
{
continents = new List();
InitializeData();
}
private void InitializeData()
{
continents.Add(new ContinentDetails()
{
ContinentName = "Asia",
Area = 43820000,
PercentOfTotalLandmass = 29.5,
TotalPopulation = 3879000000,
PercentOfTotalPopulation = 60,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Africa",
Area = 30370000,
PercentOfTotalLandmass = 20.4,
TotalPopulation = 922011000,
PercentOfTotalPopulation = 14,
});
continents.Add(new ContinentDetails()
{
ContinentName = "North America",
Area = 24490000,
PercentOfTotalLandmass = 16.5,
TotalPopulation = 528720588,
PercentOfTotalPopulation = 8,
});
continents.Add(new ContinentDetails()
{
ContinentName = "South America",
Area = 17840000,
PercentOfTotalLandmass = 12,
TotalPopulation = 382000000,
PercentOfTotalPopulation = 6,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Antarctica",
Area = 13720000,
PercentOfTotalLandmass = 9.2,
TotalPopulation = 1000,
PercentOfTotalPopulation = 0.00002,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Europe",
Area = 10180000,
PercentOfTotalLandmass = 6.8,
TotalPopulation = 731000000,
PercentOfTotalPopulation = 11.5,
});
continents.Add(new ContinentDetails()
{
ContinentName = "Australia",
Area = 9008500,
PercentOfTotalLandmass = 5.9,
TotalPopulation = 31260000,
PercentOfTotalPopulation = 0.5,
});
}
public ContinentsListResponse GetContinents()
{
return new ContinentsListResponse()
{
Continents = continents.Select(p =>
new ContinentPopulation
{
ContinentName = p.ContinentName,
TotalPopulation = p.TotalPopulation
}).ToList()
};
}
public ContinentDetailsResponse GetContinentDetails(ContinentDetailRequest request)
{
ContinentDetails continentDetails =
continents.First(p => p.ContinentName.Equals(request.ContinentName));
return new ContinentDetailsResponse()
{
Details = new ContinentDetails
{
Area = continentDetails.Area,
ContinentName = continentDetails.ContinentName,
PercentOfTotalLandmass = continentDetails.PercentOfTotalLandmass,
PercentOfTotalPopulation = continentDetails.PercentOfTotalPopulation,
TotalPopulation = continentDetails.TotalPopulation,
}
};
}
}[/cc]
Conclusion
We now have a WCF service that gets and returns JSON objects which can be used on the client-side of an application. You can easily configure
such a service to use in your applications. A WCF service that passes and receives data in JSON format is universal and can be used in both Web and Windows Forms applications.
In the next article, I would like to describe the infrastructure and implementation of AJAX calls which use JSON to a WCF service.