Javascript ETF Screener in C# .NET by Nathan Davis - ETF Screener and tool to rank ETFs by performance and trend indicators

Using Timestamp type for concurrency with MVC apps

by Nathan Davis 12. February 2009 23:11

The Timestamp datatype in SQL Server is perhaps the most misunderstood data type. The name implies that it is some kind of datetime that the database engine automatically 'stamps' into a column. In reality, it is the same as rowversion and is not translatable into a DateTime in .NET. It actually is usually translated into a byte[] or in Linq to SQL a System.Data.Linq.Binary type.

Recently, I had to implement a concurrency strategy on a project in which we are using NHibernate as our data persistence / ORM solution and using ASP.NET MVC at the Web layer. (Lets not get into a debate about the virtues of different concurrency strategies or whether is is even necessary at all - It was a requirement)

I needed to store the Version field (each table that needs concurrency checking has a timestamp column called Version) on edit forms so that when the record is sent back to the Controller, the object to be updated by NHibernate would be populated with that Version field as it was when I first got the record from NHibernate. Then when NHibernate tries to update that record, if the record's Version has changed since when I first got it, it throws a StaleObjectException and I report back to the user that they must refresh their screen and try again. This is called Version-based Optimistic Concurrency. Maybe you have another name for it.

My first stab at getting this round-trip of the Version field did not work so well. I didn't think it would, but I thought just maybe MVC's built in Hidden helper method would have some trick up its sleeve for dealing with a byte[]. It tried:

<%= Html.Hidden("Version") %>

Which renders as this HTML:

<input id="Version" name="Version" type="hidden" value="System.Byte[]" />

 

Well, that obviously won't magically bind back into a byte[]. So, I had to come up with something else. The answer was a couple of extension methods on MVC's HtmlHelper and on String. To get the byte[] into a plain string format I decided that I needed a format that split out each byte and ToString’d each. With a little inspiration from this dude I came up with a set of MVC HtmlHelper extension methods. I really like using the IEnumerable<T>.Aggregate method for this kind of stuff.

   1: public static partial class HtmlHelpers
   2: {
   3:     public static string VersionHiddenField(this HtmlHelper helper, string name, byte[] value)
   4:     {
   5:         string strValue = value.Aggregate(new StringBuilder(), (final, p) => 
   6:                             final.Append(p).Append("|"))
   7:                             .ToString()
   8:                             .TrimEnd('|');
   9:         return helper.Hidden(name, strValue);
  10:     }
  11:  
  12:     public static string VersionHiddenField(this HtmlHelper helper, string name, System.Data.Linq.Binary value)
  13:     {
  14:         return helper.VersionHiddenField(name, value.ToArray());
  15:     }

In my View page now the helper for the Version property looks like this:

<%= Html.VersionHiddenField("VersionAsString", Model.Version) %>

Notice I had to name the element “VersionAsString” – I didn’t want my controller to try to bind to the Version property. That would result in an error. So I needed a different way to get the VersionAsString back into my byte[] Version property (or Binary if you’re using LinqToSql). For this I created a set of String extension methods:

   1: public static byte[] SplitToByteArray(this string value)
   2: {
   3:     string[] arr = value.Split('|', ',');
   4:     int i = 0;
   5:     return arr.Aggregate(new byte[arr.Length], (final, p) =>
   6:                 {
   7:                     final[i] = Convert.ToByte(arr[i]);
   8:                     i++;
   9:                     return final;
  10:                 });
  11: }
  12:  
  13: public static System.Data.Linq.Binary SplitToBinary(this string value)
  14: {
  15:     return new System.Data.Linq.Binary(value.SplitToByteArray());
  16: }

Now, in my controller action method, I can get the value from the VersionAsString as a byte[] like this:

entity.Version = Request.Form["VersionAsString"].SplitToByteArray();

Once the entity gets set to the Version value from the form and I try to save it, NHibernate does the rest of the concurrency checking for me and spits out a StaleObjectException if the entity is now out-of-date.

Let me know if you know of a better way to do this.

 

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,



Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading



Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010 Nathan W Davis' Blog
Technorati Profile