Pre-filtering and shaping OData feeds using WCF Data Services and the Entity Framework - Part 1
The Open Data Protocol, referred to as OData, is a new
data-sharing standard that breaks down silos and fosters
an interoperative ecosystem for data consumers (clients)
and producers (services) that is far more powerful than
currently possible. It enables more applications to make
sense of a broader set of data, and helps every data
service and client add value to the whole ecosystem.
WCF Data Services (previously known as
ADO.NET Data Services), then, was the first Microsoft
technology to support the Open Data Protocol in Visual
Studio 2008 SP1. It provides developers with client
libraries for .NET, Silverlight, AJAX, PHP and Java.
Microsoft now also supports OData in SQL Server 2008 R2,
Windows Azure Storage, Excel 2010 (through PowerPivot),
and SharePoint 2010. Many other other applications in the
works. *
This post walks you through how to create an OData
feed, define a shape for the data and pre-filter the data
using Visual Studio 2010, WCF Data Services and the Entity
Framework. A sample project is attached at the bottom of
Part 2
of this post.
Pre-filtering and shaping OData feeds using WCF Data
Services and the Entity Framework - Part 2
Create the Web Application
File –› New –› Project, Select
“ASP.NET Empty Web Application”
Add the Entity Data Model
Right click
on the Web Application in the Solution Explorer and select
“Add New Item..”
Select “ADO.NET Entity Data Model”
under "Data”. Name the Model “Northwind” and click “Add”.
In the “Choose Model Contents”, select “Generate
Model From Database” and click “Next”
Define a connection to your database containing
the Northwind database in the next screen.
We are going to expose the Products table through our OData feed. Select “Products” in the “Choose your Database Object” screen.
Click “Finish”. We are done creating our Entity Data Model. Save the Northwind.edmx file created.
Add the WCF Data Service
Right click
on the Web Application in the Solution Explorer and select
“Add New Item..”
Select “WCF Data Service” from the
list and call the service “DataService” (creative, huh?).
Click “Add”.
Enable Access to the Data Service
Open the DataService.svc.cs class. The class is well
commented and instructs us on the next steps.
public class DataService : DataService< /* TODO: put your data source class name here */ >
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
Replace the comment that starts with “/* TODO:” with
“NorthwindEntities” (the entity container name of the Model
we created earlier).
WCF Data Services is initially locked down by default,
FTW! No data is exposed without you explicitly setting it. You
have explicitly specify which Entity sets you wish to expose
and what rights are allowed by using the
SetEntitySetAccessRule. The
SetServiceOperationAccessRule
on the other hand sets rules for a specified
operation.
Let us define an
access rule to expose the Products Entity we created
earlier. We use the
EnititySetRights.AllRead
since we want to give read only access. Our modified code is
shown below.
public class DataService : DataService<NorthwindEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Products", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
We are done setting up our ODataFeed! Compile your project.
Right click on DataService.svc and select “View in Browser”
to see the OData feed.
To view the feed in IE, you must make sure that "Feed
Reading View" is turned off. You set this under Tools -›
Internet Options -› Content tab.
If you navigate to “Products”, you should see the
Products feed. Note also that URIs are
case sensitive. ie. Products work but
products doesn’t.
Filtering our data
OData has a
set of system query operations you can use to perform common
operations against data exposed by the model. For example,
to see only Products in CategoryID 2, we can use the
following request:
/DataService.svc/Products?$filter=CategoryID eq 2
At the time of this writing,
supported operations
are $orderby, $top, $skip, $filter, $expand, $format†,
$select, $inlinecount.
Pre-filtering our data using Query Interceptors
The Product feed currently returns all Products. We want to
change that so that it contains only Products that have not
been discontinued.
WCF introduces the concept of
interceptors
which allows us to inject custom validation/policy logic
into the request/response pipeline of a WCF data service.
We will use a QueryInterceptor to pre-filter the data
so that it returns only Products that are not discontinued.
To
create
a QueryInterceptor, write a method that returns an
Expression<Func<T, bool>>
and mark it with the QueryInterceptor
attribute
as shown below.
[QueryInterceptor("Products")]
public Expression<Func<Product, bool>> OnReadProducts()
{
return o => o.Discontinued == false;
}
Viewing the feed after compilation will only show products that have not been discontinued. We also confirm this by looking at the WHERE clause in the SQL generated by the entity framework.
SELECT
[Extent1].[ProductID] AS [ProductID],
...
...
[Extent1].[Discontinued] AS [Discontinued]
FROM [dbo].[Products] AS [Extent1]
WHERE 0 = [Extent1].[Discontinued]
Other examples of Query/Change interceptors can be seen
here
including an example to filter data based on the identity of
the authenticated user.
We are done pre-filtering our data. In the next part
of this post, we will see how to shape our data.
Pre-filtering and shaping OData feeds using WCF Data
Services and the Entity Framework - Part 2
Foot Notes
* http://msdn.microsoft.com/en-us/data/aa937697.aspx
† $format did not work for me. The way to get a Json response is to include the following in the request header “Accept: application/json, text/javascript, */*” when making the request. This is easily done with most JavaScript libraries.