Managing the Data Service Context (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.

The DataServiceContext class encapsulates operations that are supported against a specified data service. Although OData services are stateless, the context is not. Therefore, you can use the DataServiceContext class to maintain state on the client between interactions with the data service in order to support features such as change management. This class also manages identities and tracks changes.

Merge Options and Identity Resolution

When a DataServiceQuery<TElement> is executed, the entities in the response feed are materialized into objects. For more information, see Object Materialization. The way in which entries in a response message are materialized into objects is performed based on identity resolution and depends on the merge option under which the query was executed. When multiple queries or load requests are executed in the scope of a single DataServiceContext, the WCF Data Services client only tracks a single instance of an object that has a specific key value. This key, which is used to perform identity resolution, uniquely identifies an entity.

By default, the client only materializes an entry in the response feed into an object for entities that are not already being tracked by the DataServiceContext. This means that changes to objects already in the cache are not overwritten. This behavior is controlled by specifying a MergeOption value for queries and load operations. This option is specified by setting the MergeOption property on the DataServiceContext. The default merge option value is AppendOnly. This only materializes objects for entities that are not already being tracked, which means that existing objects are not overwritten. Another way to prevent changes to objects on the client from being overwritten by updates from the data service is to specify PreserveChanges. When you specify OverwriteChanges, values of objects on the client are replaced by the latest values from the entries in the response feed, even if changes have already been made to these objects. When a NoTracking merge option is used, the DataServiceContext cannot send changes made on client objects to the data service. With this option, changes are always overwritten with values from the data service.

Managing Concurrency

OData supports optimistic concurrency that enables the data service to detect update conflicts. The data service provider can be configured in such a way that the data service checks for changes to entities by using a concurrency token. This token includes one or more properties of an entity type that are validated by the data service to determine whether a resource has changed. Concurrency tokens, which are included in the eTag header of requests to and responses from the data service, are managed for you by the WCF Data Services client. For more information, see Updating the Data Service.

The DataServiceContext tracks changes made to objects that have been reported manually by using AddObject, UpdateObject, and DeleteObject, or by a DataServiceCollection<T>. When the SaveChanges method is called, the client sends changes back to the data service. SaveChanges can fail when data changes in the client conflict with changes in the data service. When this occurs, you must query for the entity resource again to receive the update data. To overwrite changes in the data service, execute the query using the PreserveChanges merge option. When you call SaveChanges again, the changes preserved on the client are persisted to the data service, as long as other changes have not already been made to the resource in the data service.

Saving Changes

Changes are tracked in the DataServiceContext instance but not sent to the server immediately. After you are finished with the required changes for a specified activity, call SaveChanges to submit all the changes to the data service. A DataServiceResponse object is returned after the SaveChanges operation is complete. The DataServiceResponse object includes a sequence of OperationResponse objects that, in turn, contain a sequence of EntityDescriptor or LinkDescriptor instances that represent the changes persisted or attempted. When an entity is created or modified in the data service, the EntityDescriptor includes a reference to the updated entity, including any server-generated property values, such as the generated ProductID value in the previous example. The client library automatically updates the .NET Framework object to have these new values.

For successful insert and update operations, the state property of the EntityDescriptor or LinkDescriptor object associated with the operation is set to Unchanged and the new values are merged by using OverwriteChanges. When an insert, update, or delete operation fails in the data service, the entity state remains the same as it was before SaveChanges was called, and the Error property of the OperationResponse is set to an DataServiceRequestException that contains information about the error. For more information, see Updating the Data Service.

Setting the HTTP Method for Updates

By default, the .NET Framework client library sends updates to existing entities as MERGE requests. A MERGE request updates selected properties of the entity; however the client always includes all properties in the MERGE request, even properties that have not changed. The OData protocol also supports sending PUT requests to update entities. In a PUT request, an existing entity is essentially replaced with a new instance of the entity with property values from the client. To use PUT requests, set the ReplaceOnUpdate flag on the SaveChangesOptions enumeration when calling SaveChanges.

Note

A PUT request will behave differently than a MERGE request when the client does not know about all properties of the entity. This might occur when projecting an entity type into a new type on the client. It might also occur when new properties have been added to the entity in the service data model and the IgnoreMissingProperties property on the DataServiceContext is set to true to ignore such client mapping errors. In these cases, a PUT request will reset any properties that are unknown to the client to their default values.

POST Tunneling

By default, the client library sends create, read, update, and delete requests to an OData service by using the corresponding HTTP methods of POST, GET, PUT/MERGE/PATCH, and DELETE. This upholds the basic principles of Representational State Transfer (REST). However, not every Web server implementation supports the full set of HTTP methods. In some cases, the supported methods might be restricted to just GET and POST. This can happen when an intermediary, like a firewall, blocks requests with certain methods. Because the GET and POST methods are most often supported, OData prescribes a way to execute any unsupported HTTP methods by using a POST request. Known as method tunneling or POST tunneling, this enables a client to send a POST request with the actual method specified in the custom X-HTTP-Method header. To enable POST tunneling for requests, set the UsePostTunneling property on the DataServiceContext instance to true.

See also