This blog post is an introduction to the projection server capabilities we have added in the ADO Data Services v1.5 CTP2 release.

Introduction to Projections

Beginning in the ADO.NET Data Services v1.5 CTP2 release, the ADO.NET Data Services URI format has been extended to express narrowing projections. In particular, the projections feature allows you to work with a subset of an entity’s properties. This CTP release includes support for both the server and client (.NET only in this CTP, but Silverlight support is coming soon) runtimes. In this post, we’ll walk through how to get started using the new server side projections support using the ADO.NET Data Services URI format. In subsequent posts, we’ll take a look at the support for projections in the client library as well as how data round trips between the client and server when using projections.

The projections feature allows clients to address only the relevant properties to a specific request. By only requesting the applicable subset of an entity’s properties for a particular query, client applications can optimize for bandwidth consumption and memory footprint more effectively by fine tuning the payloads across various application scenarios.

The Projections feature extends the ADO.NET Data Services URI format by adding the $select query option to enable clients to explicitly define the properties to be returned. The $select query option may be used to reduce entity size by explicitly selecting primitive, complex type, and navigation properties on the given entity set. For example, the following usage of $select will only include the CustomerID and ContactName properties of the entities in the Customers entity set.

nw.svc/Customers?$select=CustomerID,ContactName

Walkthrough

The following is a walkthrough of using the projections feature on a data service.

To get started, you'll want to download all of the required software I use in the walkthrough: 

· Visual Studio 2008 SP1 (here)

· ADO.NET Data Services v1.5 CTP2 (here)

First, within Visual Studio create a new web application project (named ProjectionsDemo).

clip_image001

Our first data access related task is to generate the Entity Framework-based data access layer to the Northwind database.  Right click the ASP.NET project and select 'Add New Item', then select 'ADO.NET Entity Data Model'.  Name the new item nw.edmx:

clip_image002

After clicking ‘Add’ on the screen above, a wizard will open to walk you through creating the Entity Data Model for the Northwind database. Use the default settings until you get to the point where you choose the database objects to include in the model. For this demo, choose only the Customers, Orders, Order Details, and Products tables.

clip_image003

Once you reach the ‘Choose Your Database Objects’ screen, select the two tables and click ‘Finish’. This will open the Entity Data Model designer view.  This view allows you to customize the generated conceptual data model. To learn more about the mapping capabilities of the Entity Framework, see here. For this demo rename the Customers, Orders, Order_Details, and Products entities to Customer, Order, OrderDetail, and Product. Then, rename the entity sets to Customers, Orders, OrderDetails, and Products. Lastly, rename the Order_Details navigation properties on the Order and Product entity types to OrderDetails. The resulting model should look like the following diagram.

clip_image004

Create a v1.5 CTP2-based ADO.NET Data Service over this model. To create the data service, right click the ASP.NET project and select 'Add New Item'.  Add an 'ADO.NET Data Service v1.5 CTP2' item called nw.svc.

clip_image005

This will generate a file (nw.svc.cs) which represents the skeleton of a v1.5 data service. Next, we need to point the data service at the data model to be exposed as a REST-service and enable the projections feature. The snippet below shows this initialization. One thing to note is that a data service is locked down by default, so we need to take explicit steps to open access to it. For this simple application we’ll enable read and write access to the entire model quickly using the call to ‘SetEntitySetAccessRule’ shown below. Then, we enable projections by setting AcceptProjectionRequests to true, and enabling the V2 protocol. Note, when enabling projections as shown below, enabling the V2 protocol is required whereas setting AcceptProjectionRequests is optional as it is set to true by default.

clip_image006

The data service is now created. To test this, run the project and navigate to the nw.svc file. You should see a listing as shown below that outlines all the entry points to the data service. If you don’t, update IE’s settings and reload.

clip_image007

Now, let’s try our first projection over the Customers entity set on the nw.svc data service. For primitive and complex type properties, a selection of a particular property will cause its value to be included in the response. For example, to include only the CustomerID and ContactName properties for the Customers entity set, the following may be used:

nw.svc/Customers?$select=CustomerID,ContactName

clip_image008

As shown in the response above, only the CustomerID and ContactName properties are returned across the Customers entity set. In general, a query that does not include usage of $select identifies all the data to be returned whereas $select simply narrows that result.

The equivalent response in JSON for the first entry for the query above is also shown below:

{"d" : [
    {
        "__metadata": {
            "uri": "http://127.0.0.1:42218/nw.svc/Customers(\'ALFKI\')", "type": "NorthwindModel.Customer"
        },
        "CustomerID": "ALFKI",
        "ContactName": "Mary Anders"
    }
]}
Complex type properties may also be selected using the select query option.  For example, if your model contained a complex Address type that defines a Street, City, and Zip, to select the CustomerID and Address complex type, the query below may be used. When complex types are selected, all properties defined on that complex type property are returned. As a result, this query returns all components of the complex Address type: Street, City, and Zip.

nw.svc/Customers?$select=CustomerID,Address

The select option also enables the projection of navigation properties. Selecting a navigation property causes the link to the corresponding entity (or entity set) to be included in the result set. For example, the following query will result in a selection of the CustomerID property in addition to a link to the collection of order entities for each customer.

nw.svc/Customers?$select=CustomerID,Orders

clip_image009

For the Orders property referenced in the above query, the select option above will only output a link to the corresponding entity set. However, if related entities are expanded underneath the selected navigation property, all expanded descendants will be included in the response for the selected navigation property. For example, the following will select the CustomerID as well as the Orders property with fully expanded Orders and OrderDetails inlined within the response.

nw.svc/Customers?$select=CustomerID,Orders&$expand=Orders/OrderDetails&$top=3

clip_image010

When using the $select query operator with navigation properties, individual properties of the defining entity type for that navigation property may be selected as well. For example, the following query will select the CustomerID on the Customers entity set with inlined Orders that only include the OrderID property.

nw.svc/Customers?$select=CustomerID,Orders/OrderID&$expand=Orders&$top=3

The $select query option also introduces the * syntax for including all properties on a given entity or entity set. This syntax may be used to reference all properties of the current top level entity (or entity set) or all properties of a navigation property. In other words, the * syntax causes all entity data to be included without traversing associations. For example, the following select option includes all properties of each customer entity.

nw.svc/Customers?$select=*

clip_image011

The * selection may also be used on navigational properties to include all properties defined for the entity or entities associated with a navigation property. For example, the following query includes the CustomerID property of the top level Customer entity set as well as Order entities with all properties listed.

nw.svc/Customers?$select=CustomerID,Orders/*&$expand=Orders&$top=3

It is important to note that the * selection does not traverse associations. In other words, if related entities have been expanded underneath a path that uses *, the usage of * will not cause the expanded entities underneath this path to be included. For example, the following query is equivalent to the query above in that the CustomerID is included and Order entities with all properties are included; however, rather than including the fully expanded OrderDetail entities referenced in the expand clause below, each Order will contain an OrderDetails property link that references the corresponding OrderDetails entity set.

nw.svc/Customers?$select=CustomerID,Orders/*&$expand=Orders/OrderDetails&$top=3

Lastly, if a feed has customizable feed mappings, usage of $select will not impact those mappings. In other words, $select only impacts <content> and <link> content and does not impact property values mapped to other aspects of the feed.

We’ve now completed our tour of using the new projections feature on the server to work with a subset of an entity’s properties. By using the new $select query operator to request only the applicable subset of an entity’s properties for a particular query, our client applications can now more effectively optimize for bandwidth consumption and memory footprint. In subsequent posts, we’ll take a look at the support for projections in the client library as well as how data round trips between the client and server when using projections.

Aaron Dunnington

ADO.NET Data Services, Program Manager