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!