I’m working with a table that has a timestamp column in EF. I’m using this to detect when multiple people are editing the same data so that one person’s changes don’t get overwritten by another user. In the EF designer, you would mark this property as StoreGeneratedPattern=Computed
and ConcurrencyMode=Fixed
, so that the code gen knows to generate a read-only field that gets roundtripped to the server on updates. If, during an update, the timestamp value that gets roundtripped to the server differs from the one in the database, then an OptimisticConcurrencyException
is thrown so you have a chance to get the stored version of the data and possibly pop up a dialog that allows you to merge the changes together.
However, I have come across a gotcha when working with RIA services. Since the timestamp field is not NULLable, RIA services marks it as [Required]
and [Editable(false)]
when generating the code. When creating a new class to add to the data store, this ends up messing with validation, so in my datagrid it pops up with “Timestamp field is required”. You can’t set the field yourself, as it will throw an exception saying that the field is read-only.
A solution is found in the comments here for Linq-to-SQL, so here’s how to do it for EF.
I didn’t bother using the EF designer, I right-clicked the file in VS and clicked “Open With…”, then clicked XML editor. This brings up the XML schema that’s used to generate all of the code. Scroll down until you can find the class, mine was <EntityType Name="Class">
. Find the property for your timestamp, mine was <PropertyType Name="Timestamp" ConcurrencyMode="Fixed" Nullable="false" ...>
. Change that Nullable="false"
to true
, and save. The RIA services generated code will no longer require you to fill in the timestamp, and the database will handle creating the timestamp for you when the object is persisted to the database.
However, there is one more gotcha – it’s still marked as a required field. There may be a better way to accomplish this, but what I did was this: I have a field called CreationTimestamp, so I used the partial OnCreationTimestamp()
method:
partial void OnCreationTimestampChanged()
{
if (Timestamp == null)
Timestamp = new byte[] { };
}
This fixes the issue, for now.
Anyone know of a better way?
Very easy, after trying and trying for two days. I discovered the following.
I’m using Silvelight 5, RIA, Entity Framework 5 and my model is Code First:
public class Destination
{
public int DestinationId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Description { get; set; }
public byte[] Photo { get; set; }
public List Lodgings { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
The trick is using Fluent API
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ProductDestination());
}
}
internal class ProductDestination : EntityTypeConfiguration
{
public ProductDestination()
{
this.Property(t => t.RowVersion)
.IsOptional(); //Here the trick
}
}
Now you can create a new record and saved it without getting the error rowversion is required or timespam field is required.
Enjoy!
Thanks!