MVC is by definition stateless so how do you deal with timestamp that needs to be persisted across post backs in order to be used for concurrency checking?
Here’s how I’m achieving this using EF4.1 (RC), Code First and MVC 3.
I have a class, Blog.
Blog has a byte array property called TimeStamp, that I’ve marked with the code first DataAnnotation, Timestamp:
[TimeStamp] public byte[] TimeStamp{get; set;}
If you letting CF generate the database you’ll get the following field as a result of the combination of byte array and TimeStamp attribute:
So the database will do the right thing thanks to the timestamp (aka rowversion) data type – update that property any time the row changes.
EF will do the right thing, too, thanks to the annotation – use the original TImeStamp value for updates & deletes and refresh it as needed –if it’s available, that is.
But none of this will work out of the box with MVC. You need to be sure that MVC gives you the original timestamp value when it’s time to call update.
So…first we’ll have to assume that I’m passing a blog entity to my Edit view.
public ActionResult Edit(int id) { var blogToEdit=someMechanismForGettingThatBlogInstance(); return View(blogToEdit); }
In my Edit view I need to make sure that the TimeStamp property value of this Blog instance is known by the form.
I’m using this razor based code in my view to keep track of that value:
[after posting this, Kathleen Dollard made me think h arder about this and I realized that MVC 3 provides an even simpler way thanks to the HiddenFor helper…edits noted]
@Html.Hidden("OriginalTimeStamp",Model.TimeStamp)@Html.HiddenFor(model => model.TimeStamp)
This will ensure that the original timestamp value stays in the timestamp property even though I’m not displaying it in the form.
When I post back, I can access that value this way:
[HttpPost] public ActionResult Edit(byte[] originalTimeStamp, Blog blog) {
When I post back, Entity Framework can access still get at the original timestamp value in the blog object that’s passed back through model binding.
[HttpPost]
public ActionResult Edit(Blog blog)
{
Then, in the code I use to attach that edited blog to a context and update it, I can grab that originalTimeStamp value and shove it into the blog instance using Entry.Property.OriginalValue, like so, EF will recognize the timestamp value and use it in the update. (No need to explicitly set the original value now that I’ve already got it.)
db.Entry(blog).State = System.Data.EntityState.Modified;db.Entry(blog).Property(b => b.TimeStamp).OriginalValue = originalTimeStamp;db.SaveChanges();
After making the Dollard-encouraged modification, I verified that the timestamp was being used in the update:
set [Title] = @0, [BloggerName] = @1, [BlogDetail_DateCreated] = @2, [BlogDescription] = @3
where (([PrimaryTrackingKey] = @4) and ([TimeStamp] = @5))
select [TimeStamp]
from [dbo].[InternalBlogs]
where @@ROWCOUNT > 0 and [PrimaryTrackingKey] = @4′,N’@0 nvarchar(128),@1 nvarchar(10),@2 datetime2(7),@3 nvarchar(max) ,@4 int,@5 binary(8)’,@0=N’My Code First Blog’,@1=N’Julie’,@2=’2011-03-01 00:00:00′,@3=N’All about code first’,@4=1,@5=0x0000000000001771
You can use the same pattern with any property that you want to use for concurrency even if it is not a timestamp. However, you should use the [ConcurrencyCheck] annotation. The Timestamp annotation is only for byte arrays and can only be used once in a given class.
If you use ConcurrencyCheck on an editable property, you’ll need to use a hidden element (not hiddenfor) to retain the value of that property separately, otherwise EF will use the value coming from the form as the original property then grab it s a parameter of the Edit post back method and finally, use Entry.Property.OriginalValue (all that crossed out stuff ) to shove the value back into the entity before saving changes.
The next video in my series of EF4.1 videos for MSDN (each accompanied by articles) is on Annotations. It’s already gone through the review pipeline and should be online soon. It does have an example of using the TimeStamp annotation but doesn’t go into detail about using that in an MVC 3 application, so I hope this additional post is helpful.
Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!
How do i bind RowVersion model value to plain Input hidden field instead of html helper