This is the sixth article in our "Bring the clouds together: Azure + Bing Maps"
series. You can find a preview of live demonstration on
http://sqlazurebingmap.cloudapp.net/. For a list of articles in the series,
please refer to
previous post, we introduced how to access spatial data using
ADO.NET Entity Framework. This works well if you're working on a simple
N-tire solution, and all tires are based on .NET. But very often, a
cloud application must talk to the remaining of the world. For example,
you may want to expose the data to third party developers, and allow
them to use whatever platform/technology they like. This is where web
service comes to play, and this post will focus on how to expose the
data to the world using WCF Data Services.
Before reading, we assume you have a basic understanding of the
Before you go, please keep in mind that WCF Data Services are WCF
services. So everything you know about WCF applies to WCF Data Services.
You should always think in terms of service, rather than data accessing.
A data service is a service that exposes the data to the clients, not a
data accessing component.
A WCF Data Service is actually a REST service with a custom service host
(which extends WCF's WebServiceHost). You can do everything using a
regular WCF REST service. But the benefit of using data services are:
With a regular WCF REST service, you will have to implement everything on
There're 3 kinds of data providers, as listed in
Framework provider, reflection provider, and custom provider.
The simplest way to create a WCF Data Service is to use Entity Framework
as the data provider, as you can find in most tutorials. But you must be
aware of the limitations.
When using Entity Framework as the data provider for a WCF Data Service,
the service depends heavily on the EF conceptual model, and thus storage
model as well. That means if you have custom properties in the model,
like our sample's model, those custom properties will not be exposed to
This is unfortunately a limitation in the current version of data
service. There's no workaround except for using a reflection provider or
custom provider, instead of EF provider.
Our sample uses reflection provider, because it meets our requirement.
If you need more advanced features, such as defining a custom metadata,
you can use custom providers. Please refer to
http://msdn.microsoft.com/en-us/library/ee960143.aspx for more
You can think reflection provider as a plain CLR object model. For a
read only provider, you can take any CLR class, as long as they meet the
For example, our EF model exposes the Travel class. To make it compatible
with reflection provider, we perform two tweaks. First put the PartitionKey and
RowKey to the DataServiceKey list, and then put EF specific properties such as
EntityState and EntityKey to the IgnoreProperties list, because they cannot be
serialized by DataContractSerializer, and they have no meaning to the clients.
We put the binary GeoLocation property in the IgnoreProperties list as well,
because we don't want to expose it to the clients.
"PartitionKey", "RowKey" })]
Then create a service context class which contains a property of type
IQueryable<T>. Since we're using Entity Framework to perform data accessing, we
can simply delegate all data accessing tasks to Entity Framework.
Finally, use our own service context class as the generic parameter of the
data service class:
TravelDataService : DataService<TravelDataServiceContext>
In order for a reflection provider to support insert/update/delete, you have
to implement the IUpdatable interface. This interface has a lot of methods.
Fortunately, in most cases, you only need to implement a few of them.
Anyway, first make sure the service context class now implements IUpdatable:
TravelDataServiceContext : IUpdatable
Now let's walkthrough insert, update, and delete. Note since we're using
Entity Framework to perform data accessing, most tasks can be delegated to EF.
When an HTTP POST request is received, data service maps it to an insert
operation. Once this occurs, the CreateResource method is invoked. You use this
method to create a new instance of the CLR class. But do not set any properties
yet. In our sample, after the object is created, we also add it to the Entity
Type t =
Type.GetType(fullTypeName + ",
object resource =
if (resource is
to create resource. See the inner exception for more details.", ex);
Then data service iterates through all properties, and for each property,
SetValue is invoked. Here you get the property's name and value deserialized
from ATOM/JSON, and you set the value of the property on the newly created
var property =
if (property ==
property: " + propertyName);
to set value. See the inner exception for more details.", ex);
Finally, SaveChanges will be invoked, where we simply delegate the task to
Entity Framework in this case. SaveChanges will also be invoked for update and
That's all for insert. Now move on to update. An update operation can be
triggered by two types of requests:
A MERGE request: In this case, the request body may not contain all
properties. If a property is not found in the request body, then it should be
ignored. The original value in the data store should be preserved. But if a
property is found in the request body, then it should be updated.
A PUT request: Where simply every property gets updated.
To simplify the implementation, our sample only takes care of PUT. In this
case, first the original data must be queried. This is done in the GetResource
method. This method is also invoked for a delete operation. The implementation
of this method can be a bit weird, because a query is passed as the parameter,
which assumes a collection of resources will be returned. But during update and
delete, actually only one resource will be queried at a time, so you simply need
to return the first item.
q = query as
var enumerator = query.GetEnumerator();
not locate the resource.");
if (enumerator.Current ==
After GetResource, ResetResource will be invoked, and you can update its
if (resource is
Travel updated = (Travel)resource;
var original =
t => t.PartitionKey == updated.PartitionKey && t.RowKey ==
original.GeoLocationText = updated.GeoLocationText;
original.Place = updated.Place;
original.Time = updated.Time;
Finally, SaveChanges is invoked, as in the insert operation.
One final operation remaining is delete. This is triggered by a HTTP DELETE
request. It's the simplest operation. First GetResource is invoked to query the
resource to be deleted, and then DeleteResource is invoked. Finally it is
The above summaries the steps to build a reflection provider for WCF Data
Services. If you want to know more details, we recommend you to read
this comprehensive series by Matt.
Our service is now able to expose the data to the world, as well as accept
updates. But sometimes you may want to do more than just data. For example, our
sample exposes a service operation that calculates the distance between two
places. To do so, we can either create a new WCF service, or simply put the
operation in the data service.
To define a custom operation in a data service, you take the same approach as
define an operation in a normal WCF REST service, except you don't need the
OperationContract attribute. For example, we want clients to invoke our
operation using HTTP GET, so we use the WebGet attribute. We don't define a
UriTemplate, thus the default URI will be used:
double longitude1, double longitude2)
SqlGeography geography1 =
SqlGeography geography2 =
The operation itself uses spatial data to calculate the distance. Recall from
Chapter 4, if a spatial data is constructed for temporary usage, you don't
need a round trip to the database. You can simply use types defined in the
Microsoft.SqlServer.Types.dll assembly. This exactly what we're doing here.
Chapter 3, to design a scalable database, we partition the data horizontally
using PartitionKey. Now let's see it in action.
When creating the Entity Framework context, we use the overload which takes
the name of a connection string as the parameter. We choose a parameter based on
the PartitionKey. In this case, PartitionKey represents the current logged in
user (which will be implemented in later chapters). So it's very easy for us to
select the connection string.
/// Obtain the
connection string for the partition.
/// For now, all
partitions are stored in the same database.
/// But as data and
users grows, we can move partitions to other databases for better scaling.
You can test a GET operation very easily with a browser. For example, type
http://hostname/DataService/TravelDataService.svc/Travels in the browser
address bar, and you'll get an ATOM feed for all Travel entities. For POST, PUT,
and DELETE, you can use Fiddler to test them. Atlernatively, you can do as we
do, create a unit test, add a service reference, and test the operations. This
post will not go into the details about unit test. But it is always a good idea
to do unit test for any serious development.
Since our service will be hosted in Windows Azure, a load balanced
environment, it is recommended to set AddressFilterMode to Any on the service.
This post discussed how to expose data to the world using WCF Data Services.
In particular, it walked through how to create a reflection provider for WCF
Data Services. The next post will switch your attention to another cloud
service: Bing Maps. We'll create a client application that integrates both Bing
Maps and our own WCF Data Services.