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:

RTOBlogPost_RTOProperties

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 a LinqToEntitiesDomainService, any column set to ConcurrencyMode=Fixed in your Entity Framework model will result in ConcurrencyCheck and RoundTripOriginal 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 as RoundTripOriginal in order for the original values to be included in the operation.