Tracking Original Values with WCF RIA Services
Knowing what values your entity had when it was loaded on the client can be very valuable. It allows you to write server-side business logic based on specific property updates, and also allows you to supply original values to your DAL for optimistic concurrency. Using the RoundTripOriginal
attribute on your entities and complex types allows you to specify exactly how much original data you want transmitted during a SubmitChanges()
operation. By default, RIA Services automatically generates a RoundTripOriginal
attribute on Key
, ConcurrencyCheck
, and Timestamp
properties, but there are other scenarios where you need to access the client entity’s original values.
The RoundTripOriginalAttribute MSDN topic gives a great overview of the attribute in the Remarks section. The first paragraph sums the behavior up quite nicely:
You apply the RoundtripOriginalAttribute attribute to a property in an entity when you want to send the value of that property to the server to ensure data concurrency. If this attribute is not present, the value of this member will be null in the original object sent back to the server.
Let’s take a look at this in action. First, here is my Plain Old CLR Object (POCO) entity. As you can see, I’ve defined a number of properties so we can see what happens with and without RoundTripOriginal
applied:
public class MyEntity
{
[Key]
public int Id { get; set; }
public int Integer { get; set; }
public int? NullableInteger { get; set; }
public bool Boolean { get; set; }
public bool? NullableBoolean { get; set; }
public DateTime DateTime { get; set; }
public DateTime? NullableDateTime { get; set; }
public string Word { get; set; }
}
And here’s my DomainService
that exposes the entity:
[EnableClientAccess()]
public class MyService : DomainService
{
public IQueryable<MyEntity> GetMyEntities()
{
// Helper method to create some in-memory entities.
return this.CreateMyEntities();
}
public void UpdateMyEntity(MyEntity currentMyEntity)
{
MyEntity originalMyEntity =
this.ChangeSet.GetOriginal(currentMyEntity);
// Do some calculations based on original
// and store updates in database.
}
}
When I load my entities on the client, update each property on an entity, and call SubmitChanges()
, RIA Services will call into the UpdateMyEntity()
method. If I check the value of originalMyEntity
in the debugger, I’ll see this:
As you can see, ChangeSet.GetOriginal(currentMyEntity)
has returned me an object of type MyEntity
, with all of the properties (other than the Key
) set to its default value.
But what if you want to know all of the original values for your entity? Well, in that scenario, you simply add the RoundTripOriginal
attribute to your entity class (I should note that RoundTripOriginal was only allowed on properties and fields in RIA Services V1. The ability to use the attribute on a class was added in V1 SP1.):
[RoundtripOriginal]
public class MyEntity
{
…
}
This time, when I change and submit an entity, I’ll see all of my original property values:
RoundTripOriginal
allows for more granular control as well. As I alluded to above, you can also specify RoundTripOriginal
on individual properties of your Entity:
public class MyEntity
{
[Key]
public int Id { get; set; }
[RoundtripOriginal]
public int Integer { get; set; }
public int? NullableInteger { get; set; }
public bool Boolean { get; set; }
[RoundtripOriginal]
public bool? NullableBoolean { get; set; }
[RoundtripOriginal]
public DateTime DateTime { get; set; }
public DateTime? NullableDateTime { get; set; }
[RoundtripOriginal]
public string Word { get; set; }
}
And, as expected, originalMyEntity
only contains original values for the properties that I specified:
That’s the base behavior of RoundTripOriginal
, but I wanted to call out a few other scenarios that may not be completely obvious:
- A
RoundTripOriginal
attribute is automatically generated for:Key
properties.Timestamp
properties.ConcurrencyCheck
properties.
- Adding
RoundTripOriginal
to a property does not mean you will automatically get concurrency checks on this property. You must configure your DAL to check concurrency on the property (column). If you are using aLinqToEntitiesDomainService
, any column set toConcurrencyMode=Fixed
in your Entity Framework model will result inConcurrencyCheck
andRoundTripOriginal
attributes being generated for that property. - Complex types accept
RoundTripOriginal
attributes just as entities do. However, you must also mark the complex type property on the entity asRoundTripOriginal
in order for the original values to be included in the operation.