Share via


Query Projections (WCF Data Services)

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 OData: Select System Query Option ($select).

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.

Ee473425.Important(en-us,VS.100).gif Note:
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.
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}
// 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};

In this example, the object initializer pattern is used to create a new instance of the CustmerAddress 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 table describes the behaviors when projecting results into entity and non-entity types:

Action Entity Type Non-Entity Type

Creating a new projected instance by using initializers, as in the following example:

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}
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};

Supported

Supported

Creating a new projected instance by using constructors, as in the following example:

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)
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);

A NotSupportedException is raised.

Supported

Using projection to transform a property value, as in the following example:

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}
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
            };

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.

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 (WCF Data Services).

  • 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 (WCF Data Services).

For more information, see How to: Project Query Results (WCF Data Services).

See Also

Concepts

Querying the Data Service (WCF Data Services)