WCF Data Services Providers

WCF Canonical Architecture

Any solution built on WCF must define three components
address (A), binding(B), and a contract (C) (ABC).

Address is the Endpoint URI. A sample WCF Data Service
address follows:

http://localhost:8000/Data

Often included in the address are items like protocol, port
number, and a path into the Endpoint.

Binding determines how data is encoded and transmitted over
the wire. Bindings handle encryption, transport, handshaking, and packaging.

Contract defines the shape of the data. For example: Contract
determines how WCF should map a .NET class into a format that can be packaged
and then transmitted to a client on the other side of an Endpoint. In WCF
parlance this is call Serialization.

There is some overlap between these components. So, for
example, a Contract can influence a Binding’s behavior. Many developers find
WCF difficult. I believe that much of the difficulty arises because WCF is
architected to support all distributed computing forms. WCF Data Services
simplifies how a developer works with WCF and OData. WCF Data Services provides
the Binding, Contract, and influences the Address’ formatting. Data Service developers
are left to implement more mainstream .NET classes.

WCF Data Services enforces and implements OData conventions.

OData Overview

http://www.odata.org
provides a complete OData introduction so the following summarizes OData core
ideas.

OData builds on some common Web standards. Data is
transmitted over HTTP. Create, Read, Update, and Delete (CRUD) are implement
with HTTP operations like POST and GET. OData message/data formatting leverages
Atom Publishing Protocal (AtomPub) and the JavaScript Object Notation (JSON). AtomPub
and JSON schemas and conventions are defined in the specification. The
specification also defines Endpoint (URI) navigation conventions.

WCF Data Services affixes OData onto WCF. Like all WCF
services; implementing a WCF Data Service starts with configuring a host.

Configuring a Host

WCF offer two hosting models: IIS and self-hosted. Self-hosted
means the Service runs in whatever executable a developer chooses, for example,
a Console Application or a WPF Application. The following code demonstrates
configuring the host.

Type serviceType = typeof(TestItemsDataService);
Uri baseAddress = new Uri("http://localhost:8000");
Uri[] baseAddresses = new Uri[] { baseAddress };
 
DataServiceHost host = new DataServiceHost(
    serviceType,
    baseAddresses);
 
 
host.Open();
 

DataServiceHost layers on top of WebServiceHost. The
ServiceType parameter must be of type DataService<T>. Following is the
Sample code SeriviceType definition.

[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class TestItemsDataService : DataService<TestItems>
{
    public static void InitializeService(IDataServiceConfiguration
                                config)
    {
        config.SetEntitySetAccessRule("*", EntitySetRights.All);
        config.UseVerboseErrors = true;
    }
 
}
 

There will be more ServiceType details later in the article.
InitializeService is called once during Service startup. In a more
sophisticated implementation; a developer would define multiple “Entity Sets”. I
like to think of Entity Sets as the collection Property that contains the data.
The sample code implements one Entity Set called Data. Invoking
SetEntitySetAccessRule defines how the Entity Set will be accessed and updated.
For example: a developer can restrict some EntitySets to be read-only. An
asterisk “*” is a wildcard meaning all EntitySets.

Normally a service does not define VerboseErrors or
IncludeExceptionDetailsInFaults. Since Exception details are omitted by
default; both options are helpful when debugging services. DataServiceHost
covers the WCF portion of WCF Data Services. Reflection Provider works with the
ServiceType to surface data.

ServiceType and the Reflection Provider

WCF Data Services include several Providers. The Entity Data
Model provider works with an Entity Data Model. Custom Providers are dynamic
and the Reflection Provider works with static .NET classes. The core portion of
the Data Service is comprised of the type supplied in DataService<T>. In
the example this is the TestItems class. The TestItems class definition
follows:

public class TestItems : IUpdatable
{
    static Dictionary<string,TestItem> _data = null;
 
    static TestItems()
    {
        _data = new Dictionary<string,TestItem>();
 
        _data.Add("One",new TestItem() { Id = "One", Payload = "This is One" });
        _data.Add("Two",new TestItem() { Id = "Two", Payload = "This is Two" });
 
    }
 
    public IQueryable<TestItem> Data
    {
        get { return _data.Values.AsQueryable(); }
    }
 
}
 

Typically a Service exposes a collection of data. Data
Collections must be properties on the class that return an IQueryable<T>;
like the Data Property in the sample code. A Property returning an IQueryable<T>
is called an EntitySet. As stated earlier developers can control how an
EntitySet is consumed.

When the Service starts the Reflection Provider scours the
class supplied in DataService<T> for IQueryable<T> properties and
surfaces IQueryable<T> properties as a Web Service in WCF Data Services. As
stated earlier IQueryable<T> can accept any .NET class. The only
stipulation a class supplied in IQueryable<T> is that the class must
include a DataServiceKeyAttribute like in the following example.

[DataServiceKey("Id")]
public class TestItem
{
    public TestItem()
    {
        Id = "defaultId";
        Payload = "defaultPayload";
    }
 
    public string Id { get; set; }
    public string Payload { get; set; }
}
 

The key attribute should define the class instance’s uniqueness
much like a Primary Key on a database Table defines uniqueness between records.
In the example only one class in the underlying collection can have the “One”
Id value assigned to it.

A more advanced example allowing Updates to the underlying Service
is beyond the scope of this article, but will be covered in future articles.

Once the Self-hosted service is started navigation requires
following the OData URI conventions.

Navigating the Service

The URL below navigates to all the data on the Data Property
on the TestItems class.

http://localhost:8000/Data

As long as Atom feeds are set to render as XML a snippet of
the OData like the output below will render in the browser.

<title type="text">Data</title>
<id>http://localhost:8000/Data</id>
<updated>2011-10-25T01:16:20Z</updated>
<link rel="self" title="Data" href="Data" />
- <entry>
<id>http://localhost:8000/Data('One')</id>
<title type="text" />
<updated>2011-10-25T01:16:20Z</updated>
- <author>
<name />
</author>
<link rel="edit" title="TestItem" href="Data('One')" />
<category term="Test.OData.Server.TestItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
- <content type="application/xml">
- <m:properties>
<d:Id>One</d:Id>
<d:Payload>This is One</d:Payload>
</m:properties>
</content>
</entry>

Navigating to a specific class would be as follows.

http://localhost:8000/Data('One')

A more complete introduction to OData navigation is beyond
the scope of this article, but will be covered in a future article. By default
WCF Data Services renders data in AtomPub format.

Conclusion

OData’s simplicity and common underlying Web standards have
made it a popular Data protocol in the Microsoft stack. .NET Developers who
want to build an OData Web Service should try WCF Data Services. WCF Data
Services includes several Providers that surface data from .NET classes.

Resources

Reflection
Provider

More by Author

Must Read