Managing Data Concurrency
WCF RIA Services supports optimistic concurrency to insure data consistency and relies on developers to provide the logic for handling potential conflicts that can occur when updating a data source. When you enable users to update or delete data, you should make sure that the data in the data source has not been changed by another process before you blindly update its values in the data record or delete it from the record. If you do not check whether the values have changed, you can unintentionally overwrite values set by another process and leave the data in an inconsistent state. Optimistic concurrency assumes that such conflicts are rare, but compares the value it got from the data source, and which it might change or delete, with the value that the source currently has. If they do not match, then an OptimisticConcurrencyException is thrown and must be handled.
For procedures on how to specify checking values for consistency, see How to: Enable Optimistic Concurrency Checks. For information on how to use explicit transactions to guarantee consistency, see How to: Add Explicit Transactions to a Domain Service
Using the Concurrency Attributes
By default, RIA Services does not pass the entire original entity, along with the changed values, to the data access layer to check for data concurrency. Instead, RIA Services stores and passes back only those members that are marked with the RoundtripOriginalAttribute attribute, the ConcurrencyCheckAttribute attribute, or the TimestampAttribute attribute.
Each of these attributes can be applied to properties in a metadata class, or to the metadata class itself, when working with the Entity Framework. They can also be applied directly to properties or classes of CLR types when working with POCO-defined data models. For more information, see How to: Add Metadata Classes. When you build after applying these attributes, this will result in the respective attributes showing up on the client generated code for the properties or classes. When an attribute is applied to a class instead of to a property in the class, it is equivalent to applying the attribute to all of the members of the class. The ConcurrencyCheckAttribute and the TimestampAttribute attributes also cause the RoundtripOriginalAttribute to show up on the client property or class to which they have been applied in the domain service metadata. This implementation enables you to optimize the performance of your application by specifying only those members that you want to participate in the concurrency check. For more information, see the
|If the KeyAttribute is applied to the property of an entity type, this also adds the RoundtripOriginalAttribute to the corresponding property generated on the client when the project is built. So in domain services generated from a database source with the Add New Domain Service Class wizard, this is the default behavior for the properties that serve as the entity key.|
When the RoundtripOriginalAttribute is applied, the data access layer compares the original value in the data source recoded when the data was retrieved with the current value of the data in the data source. If the values are the same, then the data in the store has not changed, and the data access layer updates or deletes the data. If the data has changed, a conflict occurs and an OptimisticConcurrencyException is thrown. Information about the conflict is stored in the EntityConflict property of the entity that no longer has current data.
If you apply the ExcludeAttribute attribute to a member in an entity that can be updated or deleted from the client, that member cannot be used for optimistic concurrency checks. When you exclude the member, the correct original value is not stored for that member, and the update operation will always fail as a result. For more information, see How to: Add Metadata Classes.
Handling the OptimisticConcurrencyException
When data changes in the object cache conflict with changes that were made in the data source after objects were added to or refreshed in the cache, the whole transaction is rolled-back. When an OptimisticConcurrencyException occurs, you should handle it by specifying whether the conflict should be resolved by preserving data in the object data (ClientWins) or by updating the object cache with the data from the data source (StoreWins). For guidance on how to do this, see Saving Changes and Managing Concurrency