Andrew Siemer's Blog

Enterprise Web Applications, ASP.NET MVC, C#, SQL Server, Architecture, & Writing
posts - 61 , comments - 145 , trackbacks - 0

My Links

News

Twitter












Tag Cloud

Archives

Post Categories

Friends

My Other Blogs

My public profiles

Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

I have been working on getting around the concurrency issues that LINQ has while working with disconnected DataContext's in an nTier environment.  I currently have a Timestamp field in all of my tables to act as a row version number to help LINQ keep track of what is going on with my data.  Up to this point I haven't had any issues as I have been working with objects that were either stored in session/cache as whole objects or I have been reconstituting the object from a new DataContext prior to updating it.  In both of these scenarios I have the current timestamp to pass back to LINQ so that LINQ is happy.

This morning I found myself in a position where I didn't have the data stored in session (and didn't want to put it there either) and I didn't want to get a fresh copy from the database to update.  These two scenarios just don't work for this situation.  In reality, they don't work more often than not!  How can I get around around this Timestamp concurrency issue?

After a great deal of research on some very cool ways to do things.  And some more research regarding methods that just don't work.  And after spending my whole morning trying to resolve this.  I finally stepped back from the project and took a look at the easiest approach.  What do you know - KISS worked to my advantage.

Using extension methods in C# 3.0 I created two methods, TimestampToString(), and StringToTimestamp().  This allowed me to easily store the timestamp as an attribute, in a hidden field, etc. so that I could easily work with it just as I would a record ID.  Here's the code:

   1:          public static string TimestampToString(this System.Data.Linq.Binary binary)
   2:          {
   3:              byte[] binarybytes = binary.ToArray();
   4:              string result = "";
   5:              foreach (byte b in binarybytes)
   6:              {
   7:                  result += b.ToString() + "|";
   8:              }
   9:              result = result.Substring(0, result.Length - 1);
  10:              return result;
  11:          }
  12:   
  13:          public static System.Data.Linq.Binary StringToTimestamp(this string s)
  14:          {
  15:              string[] arr = s.Split('|');
  16:              byte[] result = new byte[arr.Length];
  17:              for (int i = 0; i < arr.Length; i++)
  18:              {
  19:                  result[i] = Convert.ToByte(arr[i]);
  20:              }
  21:              return result;
  22:          }

If you haven't worked with extension methods yet, you should be able to copy the above code into a static class (I called mine extensions <GRIN>).  Then you can call the new methods off of the matching data type.  So when working with a Class generated by LINQ that has a timestamp property (System.Data.Linq.Binary) you could say something along the lines of Account.Timestamp.TimestampToString();  And for any string that has a TimestampToString() generated value in it you can conversely call string.StringToTimestamp().

Now I will be the first to admit that this code could be cleaned up and refined.  At this point I was just happy to get it to work.  I have seen so many examples of things that just didn't work for this scenario so I felt the need to post something that did!

Print | posted on Monday, February 11, 2008 12:04 PM | Filed Under [ ASP.NET C# LINQ ]

Feedback

Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Nice extension implementations... Could I get you to give a little more detail on exactly how you're using this? This main issue we're running into here with is that the System.Data.Linq.Binary type which when we added timestamp columns to our table are not serializable. I am a little confused on how your using the above. Did you end up removing the Timestamp columns from your table, etc.

Thanks in Advance,
David
2/20/2008 8:55 AM | David Davids
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

I did this specifically because the Linq binary isn't serializable. So what this basically does is allows you to convert the binary value into a string which in my case I store on the web page along with the record ID of the item I am working with. Then if I reach a point where I want to update that item I can easily load up the object in question, with it's last time stamp and record ID, attach it to the list of items associated with the context, Attach(Item, true), telling the context that the item has been modified with the true flag.

This of course could be done a bit differently in that with the ID you could re-get the item...copy in the new values, and save it. But there are often times that you don't need to do this and really just want to toss your new data at the context. For these scenarios...the TimestampToString works great!
2/20/2008 10:41 AM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Our proxy objects use byte[] to store the time stamp and we have not had problems with this?

The serializer converts System.Linq.Binary to byte[] seamlessly, is there something im missing here?
4/9/2008 11:29 PM | Ross Jones
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

This isn't so much about getting it to a workable value...but taking the timestamp and storing it as a string in a web page so that you can use it on postback....so that you don't then have to rehydrate the object to work with it.
4/10/2008 11:36 AM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

I have just come across this problem =) Scowered google for this post again... I now relaize why this is required.

Thanks
4/28/2008 6:47 AM | Ross Jones
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

This seems to have solved the timestamp issue for web services (for a top level entity), will try associative next. Basically - set the TimeStamp property on the DataContext Entity to Private, then extend the class you wish to detach via webservices with a single property viz:
public partial class MyEntity
{
public string VersionStamp
{
get { return _TimeStamp.TimestampToString(); }
set { _TimeStamp = value.StringToTimestamp(); }
}
}

This means the Client will always have the timestamp, and when the returning entity is deserialised, the internal object is has its private variable automatically synchronised. It seems to work, but will need more testing.
5/13/2008 8:56 AM | Edward
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

"John" - you are missing the point of this post! This is not so much about good code as it is about addressing the issues of being able to persist the Timestamp in such a way that you can reconstitute your object without having to reload it from the database. I agree with your string builder concept...that is always better. But we are not working with large amounts of data, images, etc. Just a timestamp!

As I stated in the last paragraph of my post:

"Now I will be the first to admit that this code could be cleaned up and refined. At this point I was just happy to get it to work. I have seen so many examples of things that just didn't work for this scenario so I felt the need to post something that did!"

Thank you for your interest though!
7/1/2008 2:04 PM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

John - You are a hard cat to please! This article is actually about saving the Timestamp in a page in a way that you can reconstitute your object on the next view rather than having to reload it from the database. By all means if you have a better way to do this or a more efficient way to do the same thing...please feel free to post it here as a comment or in your blog (and link to that post here!). I do very much appreciate your fervor though!
7/3/2008 11:35 AM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

I think this is what you are after...

'Converting From Linq Binary to String
TimeStampString = Convert.ToBase64String(TimeStampBinary.ToArray)

'Converting From String to Linq Binary
TimeStampBinary = new Binary(Convert.FromBase64String(TimeStampString))

I know it's VB, but can't we all just convert VB/C# in our heads now...
7/10/2008 3:57 PM | Kevin
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Now thats a good solution. Thanks Kevin
8/18/2008 12:44 PM | Gribin
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Now thats a good solution Kevin. I struggled with the conversion to C#. Maybe Andrew could write some complicated mechanism to make the conversion and post it on his next blog.
8/18/2008 12:48 PM | Gribin
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

You blokes are harsh! I thought the idea of a blog such as this was to provide a forum to inspire solutions to problems. Kevin's is another solution, which certainly is more concise, but it was Andrew's original blog entry that provided the impetus for him to provide it. So rather than castigate him for code he provided with a caveat that identified a need for some re-working, I suggest you reserve an equal measure of thanks for Andrew as any subsequent poster with a re-worked solution.
8/25/2008 11:13 PM | Barry
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Andrew,

Don't let the previous posts discourage you... I've been having exactly the same problem with timestamps and looked for over 2 weeks for a solution (other than the workarounds you mentioned). Even several newsgroup postings could not help...

So, thanks a lot for this posting! :-)
8/29/2008 5:04 AM | Adrian
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Looks like the easiest way to do this is completely different after all: Simply add your Linq timestamp attribute name to the "DataKeyNames" attribute of the DetailsView, GridView, etc. I just stumbled across this solution by coincidence, but it seems to work fine.
9/17/2008 6:14 AM | Adrian Grigore
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

According to my good buddy Frank Wang you can change the data type of the Timestamp field in your design surface to a byte array which may address this issue. Worth taking a look at any how!

http://geekswithblogs.net/frankw/archive/2008/08/29/serialization-issue-with-timestamp-in-linq-to-sql.aspx
9/22/2008 12:07 PM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Andrew,

Thanks for the posting. In fact your "approach" is superior to Frank's as using a byte[] suffers from the problem of not working if you try to use the .Attach(modified, original) overload. In other words, byte[] does solve the "serializable" goal, but fails in the "concurrency" stakes.
10/28/2008 11:07 AM | kiwidude
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

WOW ! WTF is going on here ?

Don't you JUST:

public static string TimestampToString(this Binary binary)
{
return BitConverter.ToUInt64(binary.ToArray(), 0).ToString();
}

public static Binary StringToTimestamp(this string s)
{
return new Binary(BitConverter.GetBytes(Convert.ToUInt64(s)));
}


EVEN BETTER using ulong - NO STRINGS:
public static ulong TimestampToUlong(this Binary binary)
{
return BitConverter.ToUInt64(binary.ToArray(), 0);
}

public static Binary UlongToTimestamp(this ulong value)
{
return new Binary(BitConverter.GetBytes(value));
}



11/5/2008 8:08 AM | Georgi Ganchev
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Georgi: I like this very much!
11/5/2008 10:05 AM | Andrew Siemer
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Hi All,

Found this of some use but, I have a slightly different problem. I need to compare the SQL Timestamp value for all the rows in a table to the value that I retrieved at an earlier time to see if it has increased (changes have been made since last timestamp) and I am not sure how to format the select statement. Been programming a Longgggg time, but am new to using the SQL timestamp field. I am doing this in a VB .NET windows forms app - not some type of stored procedure. What I need to do is something like "Select * where TimeStampcolumn > savedTimeStamp". Do I use the string representation of the savedTimeStamp in the select statement? Any help would be greatly appreciated!
TIA! - hground
11/26/2008 8:55 AM | hground
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

This looks like it's what I've been looking for to solve my problem, up until I try to recreate my LINQ entity that is. No matter how you look at it, the version property is still a Binary, so I can't assign back to it. Using the above "UlongToTimestamp" I can't take that result and assign it to my timestamp row version.

How are you recreating the LINQ entity such that it doesn't complain when you Attach(), then SubmitChanges()
12/30/2008 10:49 AM | Wes
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

I'm sure this isn't the best solution and having seen the drive for speedy code I'm sure there will be some backlash but I've got around this by adding the following property to my Linq objects.

public System.UInt64 Stamp
{
get
{
return BitConverter.ToUInt64 (this._TimeStamp.ToArray(), 0);
}
set
{
this._TimeStamp = new Binary(BitConverter.GetBytes(value));
}
}

Works fine and I can't see why a base class can't be created to handle this for all Linq Objects to save time messing around. I can't find anyway to force this to happen when you drop items into your dbml but if it's just a case of going in and adding a "inherits" line it's not such hard work.
2/25/2009 10:32 AM | Chris
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Extensions huh, maybe I should have read up first :)
2/25/2009 10:41 AM | Chris
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Chris,
I am having a similar issue (a Binary(8) NOT NULL column that LINQ maps to System.Data.Linq.Binary).
We want to map it to long, ulong or something else other than binary. Basically work with the long (or other data type). Before LINQ to SQL we used CAST(binary_field as bigint) in our sprocs...worked great...jejeje
Can you provide an example of how you implemented the public System.UInt64 Stamp?
Thanks for all your help,
Cheers
5/19/2009 6:26 PM | Jose Gonzalez
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Thanks, it was really helpful.
2/12/2010 3:11 AM | Derin
Gravatar

# re: Converting a System.Data.Linq.Binary (or timestamp) to a string and back again to avoid LINQ concurrency issues

Hello,

I has to translate this text, to have the seconds


var currentDate = new Date()
var timestamp = parseInt(currentDate.getTime()/600000) * 600000;
var timestamp_hexa = timestamp.toString(16);
8/23/2010 7:36 AM | jean
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 
 

Powered by: