Query Projections (WCF Data Services)

Important

WCF Data Services has been deprecated and will no longer be available for download from the Microsoft Download Center. WCF Data Services supported earlier versions of the Microsoft OData (V1-V3) protocol only and has not been under active development. OData V1-V3 has been superseded by OData V4, which is an industry standard published by OASIS and ratified by ISO. OData V4 is supported through the OData V4 compliant core libraries available at Microsoft.OData.Core. Support documentation is available at OData.Net, and the OData V4 service libraries are available at Microsoft.AspNetCore.OData.

RESTier is the successor to WCF Data Services. RESTier helps you bootstrap a standardized, queryable, HTTP-based REST interface in minutes. Like WCF Data Services before it, Restier provides simple and straightforward ways to shape queries and intercept submissions before and after they hit the database. And like Web API + OData, you still have the flexibility to add your own custom queries and actions with techniques you're already familiar with.

Projection provides a mechanism in the Open Data Protocol (OData) to reduce the amount of data in the feed returned by a query by specifying that only certain properties of an entity are returned in the response. For more information, see section 4.8. Select System Query Option ($select) in URI Conventions (OData Version 2.0).

This topic describes how to define a query projection, what the requirements are for entity and non-entity types, making updates to projected results, creating projected types, and lists some projection considerations.

Defining a Query Projection

You can add a projection clause to a query either by using the $select query option in a URI or by using the select clause (Select in Visual Basic) in a LINQ query. Returned entity data can be projected into either entity types or non-entity types on the client. Examples in this topic demonstrate how to use the select clause in a LINQ query.

Important

Data loss might occur in the data service when you save updates that were made to projected types. For more information, see Projection Considerations.

Requirements for Entity and Non-Entity Types

Entity types must have one or more identity properties that make up the entity key. Entity types are defined on clients in one of the following ways:

By default, when you project query results into a type defined at the client, the properties requested in the projection must exist in the client type. However, when you specify a value of true for the IgnoreMissingProperties property of the DataServiceContext, properties specified in the projection are not required to occur in the client type.

Making Updates to Projected Results

When you project query results into entity types on the client, the DataServiceContext can track those objects with updates to be sent back to the data service when the SaveChanges method is called. However, updates that are made to data projected into non-entity types on the client cannot be sent back to the data service. This is because without a key to identify the entity instance, the data service cannot update the correct entity in the data source. Non-entity types are not attached to the DataServiceContext.

When one or more properties of an entity type defined in the data service do not occur in the client type into which the entity is projected, inserts of new entities will not contain these missing properties. In this case, updates that are made to existing entities will also not include these missing properties. When a value exists for such a property, the update resets it to the default value for the property, as defined in the data source.

Creating Projected Types

The following example uses an anonymous LINQ query that projects the address-related properties of the Customers type into a new CustomerAddress type, which is defined on the client and is attributed as an entity type:

// Define an anonymous LINQ query that projects the Customers type into
// a CustomerAddress type that contains only address properties.
var query = from c in context.Customers
            where c.Country == "Germany"
            select new CustomerAddress {
                CustomerID = c.CustomerID,
                Address = c.Address,
                City = c.City,
                Region = c.Region,
                PostalCode = c.PostalCode,
                Country = c.Country};
' Define an anonymous LINQ query that projects the Customers type into 
' a CustomerAddress type that contains only address properties.
Dim query = From c In context.Customers _
            Where c.Country = "Germany" _
            Select New CustomerAddress With { _
                .CustomerID = c.CustomerID, _
                .Address = c.Address, _
                .City = c.City, _
                .Region = c.Region, _
                .PostalCode = c.PostalCode, _
                .Country = c.Country}

In this example, the object initializer pattern is used to create a new instance of the CustomerAddress type instead of calling a constructor. Constructors are not supported when projecting into entity types, but they can be used when projecting into non-entity and anonymous types. Because CustomerAddress is an entity type, changes can be made and sent back to the data service.

Also, the data from the Customer type is projected into an instance of the CustomerAddress entity type instead of an anonymous type. Projection into anonymous types is supported, but the data is read-only because anonymous types are treated as non-entity types.

The MergeOption settings of the DataServiceContext are used for identity resolution during query projection. This means that if an instance of the Customer type already exists in the DataServiceContext, an instance of CustomerAddress with the same identity will follow the identity resolution rules set by the MergeOption

The following describes the behaviors when projecting results into entity and non-entity types:

Creating a new projected instance by using initializers

  • Example:

    var query = from c in context.Customers
                where c.Country == "Germany"
                select new CustomerAddress {
                    CustomerID = c.CustomerID,
                    Address = c.Address,
                    City = c.City,
                    Region = c.Region,
                    PostalCode = c.PostalCode,
                    Country = c.Country};
    
    Dim query = From c In context.Customers _
                Where c.Country = "Germany" _
                Select New CustomerAddress With { _
                    .CustomerID = c.CustomerID, _
                    .Address = c.Address, _
                    .City = c.City, _
                    .Region = c.Region, _
                    .PostalCode = c.PostalCode, _
                    .Country = c.Country}
    
  • Entity type: Supported

  • Non-entity type: Supported

Creating a new projected instance by using constructors

  • Example:

    var query = from c in context.Customers
                where c.Country == "Germany"
                select new CustomerAddress(
                c.CustomerID,
                c.Address,
                c.City,
                c.Region,
                c.PostalCode,
                c.Country);
    
    Dim query = From c In context.Customers _
                Where c.Country = "Germany" _
                Select New CustomerAddress( _
                c.CustomerID, _
                c.Address, _
                c.City, _
                c.Region, _
                c.PostalCode, _
                c.Country)
    
  • Entity type: A NotSupportedException is raised.

  • Non-entity type: Supported

Using projection to transform a property value

  • Example:

    var query = from c in context.Customers
                where c.Country == "Germany"
                select new CustomerAddress
                {
                    CustomerID = c.CustomerID,
                    Address = "Full address:" + c.Address + ", " +
                    c.City + ", " + c.Region + " " + c.PostalCode,
                    City = c.City,
                    Region = c.Region,
                    PostalCode = c.PostalCode,
                    Country = c.Country
                };
    
    Dim query = From c In context.Customers _
                Where c.Country = "Germany" _
                Select New CustomerAddress With _
                {.CustomerID = c.CustomerID, _
                    .Address = "Full address: " & c.Address & ", " & _
                    c.City & "," & c.Region & " " & c.PostalCode, _
                    .City = c.City, _
                    .Region = c.Region, _
                    .PostalCode = c.PostalCode, _
                    .Country = c.Country}
    
  • Entity type: This transformation is not supported for entity types because it can lead to confusion and potentially overwriting the data in the data source that belongs to another entity. A NotSupportedException is raised.

  • Non-entity type: Supported

Projection Considerations

The following additional considerations apply when defining a query projection.

  • When you define custom feeds for the Atom format, you must make sure that all entity properties that have custom mappings defined are included in the projection. When a mapped entity property is not included in the projection, data loss might occur. For more information, see Feed Customization.

  • When inserts are made to a projected type that does not contain all of the properties of the entity in the data model of the data service, the properties not included in the projection at the client are set to their default values.

  • When updates are made to a projected type that does not contain all of the properties of the entity in the data model of the data service, existing values not included in the projection on the client will be overwritten with uninitialized default values.

  • When a projection includes a complex property, the entire complex object must be returned.

  • When a projection includes a navigation property, the related objects are loaded implicitly without having to call the Expand method. The Expand method is not supported for use in a projected query.

  • Query projections queries on the client are translated to use the $select query option in the request URI. When a query with projection is executed against a previous version of WCF Data Services that does not support the $select query option, an error is returned. This can also happen when the MaxProtocolVersion of the DataServiceBehavior for the data service is set to a value of V1. For more information, see Data Service Versioning.

For more information, see How to: Project Query Results.

See also