Blog Stats
  • Posts - 18
  • Articles - 0
  • Comments - 53
  • Trackbacks - 0

 

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!


Feedback

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar That code is bad.

From a performance point of view you should have used the StringBuilder class. it's always faster in the general case, and since you're doing about 8 concat operations a StringBuilder is much better.

Also, I don't see the point of serializing the object as a list of numbers you could just as easily use the Convert.ToBase64String method.

More over, a Linq Binary is immutable and does defensive copying. Which means, every time you do .ToArray on a Linq.Binary you create a complete copy of that object. timestamp values are relatively small so that's perfectly fine. But you'd be wise to not use the Binary.ToArray method if you're writing lots of data to the output stream (like images).

I'm currently facing a similar problem here and the culprit is really this Linq.Binary data type, which I don't like at all. To why the ToString method adds &qout; around the returned string is beyond me. 7/1/2008 1:52 PM | John

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

Gravatar "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

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

Gravatar Yeah, I did read it. It was more to explain the internal workings of the Linq.Binary. And you're using an excessive amount of string concatenations and other significantly slower methods. I personally wouldn't encourage that type of code no matter what, that's partly why I mentioned it.

You could just as easily serialize the bytes as a hex string which would eliminate the need for pipes and string.Split(). Each byte will put two nibbles of your choice of character data and if you just pick the first 16 characters from the ASCII table "ABCDEFGHIJKLMNOP" you get a very straight forward serializing/deserializing method which wont introduce any potentially bad characters either.

You admit that this code could be cleaned up and refined. So do it! I'm a strong believer in that code should be fine-tuned and optimal. Nothing short there of.
7/1/2008 2:24 PM | John

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

Gravatar 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

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

Gravatar 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

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

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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

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

Gravatar 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

Post a comment





 

Please add 1 and 6 and type the answer here:

 

 

Copyright © Andrew Siemer - www.andrewsiemer.com