I'm back in the USA from the Bahamas, a place where Internet Access cost me 0.50USD/minute (thus I hardly used it).
A few questions have come up in the Windows Phone developer forum centered around serialization and WCF. I've been working on an introduction to WCF for WP7 developers (though do to recent travel and holidays I'm not done with it yet) and am releasing it in parts. In this first part I touch on Serialization and create a simple WCF service. I'm starting off with desktop WCF projects and then will transition to Windows Phone 7 specifics. Because WP7 supports a subset of WCF I won't venture far into WCF functionality that isn't available on WP7.

Prerequisites
I'm writing this targetting the developer that is just getting started with Windows Phone development. The developer will have already have learned the C# development language and have become comfortable with the Windows Phone application model of their choice (Silverlight or XNA). A developer in my target audience doesn't necessarily have a strong web services background. If you still need to get an understanding for Windows Phone 7 programming you might won't to check out this free learning material by Rob Miles.
Required Software
The software required for this article is Visual Studio 2010. The Express edition of Visual Studio that comes with the Windows Phone tools won't be sufficient; I'll be using som desktop programming examples in this article.
Why WCF
With the ubiquity of Internet access it's common for programs to rely on services hosted on other machines. Some times this is becaus edata consumed by the program is kept in some centralized location. Othertimes it is to distribute computational load. Having a machine on which to host a service also facilitates communication between different client instances such as Instant Messenger type interactions. What ever your reason there are a lot of scenarios for which programs may need to communicate with other machines.
Communication among different clients can be implemented in a number of different ways. Clients may communicate with a service over raw socket connections in which they send messages to the server and receive data in a format completly authored by developers. Or a client may communicate using HTTP web request, much like your browser does. It is even possible for clients to use e-mail protocols for sending messages to other services. The number of ways in which communication could be implemented is countless. What ever the method you choose it will be necessary for both the client you are developing and the service that is providing some functionality agree on the formats used for the data.
WCF, or Windows Communication Foundation provides the functionality that a developer can use to quickly implement communication between services and clients. Instead of being burdened by implementing communication at the socket level WCF allows you to specify how communication will occur in higher level terms and will take care of managing communication channels that conform to what you've specified. You can specify that you want communication to occur over certain protocols (many of which are standards complient) along with marking which elements of data or functionality will be exposed to the end user. WCF will take care of converting your data elements into a format that can be transfered ofer the connection and will take care of reassembling the new objects on the other side of the connection.
Serialization
Whether you are sending data over a network connection or saving it to storage your data needs to be serialized. Serialization is simply converting data to a format that is appropriate for these purposes. At first one may wonder why the data needs to be converter. After all the data is nothing more than bits in memory, and bits can be transmited. But without conversion thos bits may not have much meaning if transmited in an unconverted format. The bits could contain the name of a file that doesn't exists on the remote system. Those bits could contain a pointer that may not point to the same item of data on another machine (or they may not point to any relevant data on the same machine during a different session!). Also some data may not need to be transmited or saved. If I made a class representing a rectangle I may decide that I only need to serialize the Width and Height members but not the Area member (since I can always recalculate it from the other two members).
Classes that serializer our data are called serializers. The two serializers that I will discuss here are the DataContractSerializer and the XmlSerializer. A number of data types are already serializable. These include the various numeric types (double, int, byte, float and so on), string, and arrays composed of these data types. We don't need to do any extra work to be able to serialize these. It's the complex data types for which we need to do some more work. Let's start with a stereotypical employee class.
class Employee
{
public int Number { get; set; }
public string Name { get; set; }
public string Position { get; set; }
}
Without a serializer if you wanted to write entities of this type to a file you would need to decide on some way of delimiting your data and write code to place each value in your file. To read from the file you would also need to write code that would load the values from the files in the same order. If there were a change to your data type you would also need to make changes to your code for reading and writing. That amounts to a lot of busy work for a result that is in no way intelectually rewarding nor is it productive. When using a serializer things are much simpler. One only needs to instantiate a serializer telling it what type of data it will be serializing and then use the serializer to read or write the stream. The following code demonstrates what must be done. Note that this code is written to run on a desktop so that we can more easily get to the resulting file.
var e = new Employee()
{
Number = 515148,
Name = "Joel Ivory Johnson",
Position = "Owner"
};
XmlSerializer employeeSerializer = new XmlSerializer(typeof(Employee));
using(StreamWriter sw = new StreamWriter("employeeData.xml"))
{
employeeSerializer.Serialize(sw,e);
sw.Close();
}
Running this code will result in an error though. The error will say that Employee is inaccessible due to its protection level. To correct this Employee must be declared as public and then the code will work. The program's resulting file will be found in it's directory and it's content looks like the following
<?xml version="1.0" encoding="utf-8"?>
<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Number>515148</Number>
<Name>Joel Ivory Johnson</Name>
<Position>Owner</Position>
</Employee>
What if I have a property on the class that cannot be serialized? To invoke this senario I'm adding a new property of type IntPtr which is undoubtedly not serializable.
public class Employee
{
public int Number { get; set; }
public string Name { get; set; }
public string Position { get; set; }
public IntPtr X {get; set; } //This will not serializae
}
Making the change will result in an error message stating "There was an error reflecting type 'Employee'."" I don't want the pointer element serilized. By placing the [XmlIgnore] on the property and all will be well again.
There are a number of other attributes that can be placed on a class being serialized with the XmlSerializer. I won't discuss those within this article but mention it so that those that wish to have control over the resulting Xml know that there are additional options.
DataContractSerializer
The DataContractSerializer appears to work like the XmlSerializer at first glance. Let's take a look at the source for the same program if it used the DataContractSerializer instead of the XmlSerializer along with its output.
<Employee xmlns="http://schemas.datacontract.org/2004/07/J2i.Net.Example01.DataContractSerialization" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Name>Joel Ivory Johnson</Name>
<Number>515148</Number>
<Position>Owner</Position>
<X xmlns:a="http://schemas.datacontract.org/2004/07/System"><value i:type="b:long" xmlns="" xmlns:b="http://www.w3.org/2001/XMLSchema">0</value></X>
</Employee>
The data contract serializer was able to serialize the X member, but I don't want that element serialized. To solve this problem I can add the [IgnoreDataMember] attribute to the code. There's something missing though. Typically if you are designing a class to be used by the DataContracySerializer it will be decorated with the [DataContract] member. If you add the [DataContract] attribute to the class and run it again we'll end up with an empty XML files. While the XmlSerializer is an opt-out serializer (it will try to serialize everything unless told to do otherwise) the DataContractSerializer is an opt-in serializer (It won't serialize anything unless a field is marked otherwise). So to correct this we need to add [DataMember] to each one of the fields that we want serialized. In general you'll want to have some control over how your class is serialized, so serialization with the [DataContract] attribute is preferred.
The Service Contract
While the Data Contract allows you to declare constructs used for passing data arount the Service Contract let's you make declarations on functionality that will be provided. Like the data contract you will decorate a class with attributes. But instead of specifying what data is available to external entities the attributes will specify what functionality is available to external entities. For the sake of cleanly separating the specification for functionality provided from the implementation of that functionality I'll be using interfaces to declare functionality. Of course the implementation for that functionality will be in a class that inherits from that functionality.
To get started let's make a simple service that has a few pieces of simple functionality. I'll call this our Mathimatics service. This service can take two numbers and either multiply them or return the difference between the two numbers. Since the data types being passed around are simple won't need to concern ourselves with data contracts. To get started I'm creating a Windows class library that will contain the interface that defines the service contract and a class that implements the service. The interface is decorated with the [ServiceContract] attribute and the methods on the interface are decorated with the [OperationContract] attribute.
[ServiceContract]
public interface IMathimaticsService
{
[OperationContract]
double Multiply(double arg1, double arg2);
[OperationContract]
double Difference(double arg1, double arg2);
}
class MathimaticsService: IMathimaticsService
{
public double Multiply(double arg1, double arg2)
{
return arg1*arg2;
}
public double Difference(double arg1, double arg2)
{
return Math.Abs(arg1 - arg2);
}
}
The code will compile just fine but in its present state you can't run it. You could create another project and add the assembly produced by the above as a reference, but that would be going against the point of a service. With a service you want your functionality on one machine or process and the client using the functionality is generally in another machine or another process. To make use of this code we'll actually need two more projects. We need a project that will host the functionality and another project that will make use of the functionality. The service can be hosted in just about any Windows application; web application, Windows Form application, console application, and so on. Though generally one will dichotomize the hosting options to web or desktop.
I'll use a console application to host the service for now. There's still some more decisions to be made. A WCF service must always have three things defined when it is hosted; it must have an address, a binding, and a contract. Some call this the "ABCs of WCF" to make it easier to remember. The address will be the machine name or IP address along with the port over which the service will communicate. In most of the examples that follow the address will be localhost:8123. Though when running a client from an actual Windows Phone you'll want to have your computer name in that place. The binding defines how communication will occur. Presently Windows Phone only supports HTTP based communication. So I'll onl be using basic HTTP binding (HTTPS is also supported, but I won't cover setting up your machine to do secure communication). We've already covered what a contract is.
What's an endpoint?
The console application will instantiate a new ServiceHost with the our address and set the binding for it. We want other potential clients to be able to inquire about our services contracts. So we'll need to add a metadata exchange endpoint. With the metadata endpoint added other development tools will be able to look at our service definition and generate code to make the service easier to use for the developer. Without it the developer has no way to know what functionality is available short of it being communicated through some other means. With all of the above defined the only thing left to do is open the communications channel by calling ServiceHost.Open(). The process will need to stay alive so that the service can continue to be available and then we should free up the service's resources by calling ServiceHost.Close().
static void Main(string[] args)
{
ServiceHost myServiceHost = new ServiceHost(typeof(MathimaticsService),
new Uri("http://localhost:8123/MathimaticsService"));
ServiceMetadataBehavior myBehavior = new ServiceMetadataBehavior(){HttpGetEnabled = true};
myServiceHost.Description.Behaviors.Add(myBehavior);
myServiceHost.AddServiceEndpoint(typeof (IMathimaticsService), new BasicHttpBinding(), String.Empty);
myServiceHost.AddServiceEndpoint(typeof (IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(),
"mex");
myServiceHost.Open();
//Keep the service from immediatly terminating by waiting on the user to press the Enter key
Console.WriteLine("Press [Enter] to close");
Console.ReadLine();
myServiceHost.Close();
}
While it would work, there's something I don't like about the above code. What would I need to do to move the code to a different machine or if I needed to host the service on a different port? I would need to change the code, recompile it, and redeploy it. That's no good. I could move the information on the port and address to an external file and read it at run time so that the program is nolonger bound to compile time settings. Microsoft has already included support to do this with the App.config file. Covering how App.config works is beyond the scope of this writing so what I discuss here in a minimum. I've added a new item to my services host project named App.config. When the project is compiled this file will be copied to a file that has the same name as the executable with .config appended to the end.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="J2i.Net.MathimaticsService" >
<host>
<baseAddresses>
<add baseAddress="http://localhost:8123/MathimaticsService"/>
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding"
contract="J2i.Net.Example02.ServiceContractExample.IMathimaticsService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
WCF is already capable of pulling this information from the App.config and using it. The code for our service is greatly simplified.
static void Main(string[] args)
{
ServiceHost myServiceHost = new ServiceHost(typeof(MathimaticsService));
myServiceHost.Open();
Console.WriteLine("Press [Enter] to close.");
Console.ReadLine();
myServiceHost.Close();
}
Tags: Windows Phone 7, Development