Frank Wang's inspirations on .NET

IEnumerable<Inspiration> inspirations = from i in DataContext.Inspirations where i.Sharable == true select i

Serialization issue with timestamp in LINQ to SQL

Friday, August 29, 2008 7:14 PM

Many of you use timestamp in LINQ to SQL because it's easier for us figure out if an object is new or not. This is specially useful when we need to track the state of objects in disconnected/N-tier scenarios.  An important thing to note, though, is that the timestamp column is mapped as a System.Data.Linq.Binary property in the LINQ generated object so it is part of the XML serialization when you use the LINQ to SQL objects in Web Services/WCF services. The real problem is that System.Data.Linq.Binary doesn't have a parameterless constructor. Take a look at the Binary's constructor documented in MSDN. There's no parameterless constructor available to the class. What this means is the serialization of timestamp will fail for sure when you pass the object around in your service calls.

C# 
public Binary(
    byte[] value
)
 

To prove my point, we can create a Web Service that returns a LINQ to SQL object with the timestamp field. First of all, let's add a timestamp column to the Products table in Northwind.

image

Save the table. Create a new Web Service project named NorthwindProductService. Add a LINQ to SQL file Northwind.dbml into the project. We only need the Products table in the dbml file for our sample to work.

DBML

Find the definition of the TimeStamp property in the LINQ generated class. You will see the type of this property is System.Data.Linq.Binary.

[Column(Storage="_TimeStamp", AutoSync=AutoSync.Always, DbType="rowversion NOT NULL", CanBeNull=false, IsDbGenerated=true, IsVersion=true, UpdateCheck=UpdateCheck.Never)]
    public System.Data.Linq.Binary TimeStamp
    {
        get
        {
            return this._TimeStamp;
        }
        set
        {
            if ((this._TimeStamp != value))
            {
                this.OnTimeStampChanging(value);
                this.SendPropertyChanging();
                this._TimeStamp = value;
                this.SendPropertyChanged("TimeStamp");
                this.OnTimeStampChanged();
            }
        }
    }
    

At last, let's add a simple web method called GetProductByID that returns a Product object from the LINQ to SQL data context.

[WebMethod]
public Product GetProductByID(int productID)
{
   return new NorthwindDataContext().Products.Where(p => p.ProductID == productID).SingleOrDefault();
}

Now run the Web Service from Visual Studio. The asmx file throws the serialization exception immediately. We haven't even got  a chance to invoke the GetProductByID method.  But the exception does make all the sense as an object without parameterless constructor cannot be serialized.

image

Don't be desperate yet. Every problem has a solution :-) LINQ to SQL has provided a way to change the data type of every property that is mapped to the actual table column. By default, LINQ uses the original type of the column. As you can see in the LINQ designer surface, the data type of TimeStamp  is automatically set to System.Data.Linq.Binary.

image

All you have to do is change the data type from System.Data.Linq.Binary to byte[], which doesn't need to be instantiated during the serialization.

image

Compile and run the Web Service again. It's no longer complaining about the serialization issue. The source code for the sample we did in this blog post can be downloaded from my Live SkyDrive.




Feedback

# re: Serialization issue with timestamp in LINQ to SQL

Frank,

The problem with this approach is that it doesn't work if you want to use the Table<T>.Attach(modified, original) overload, such as when you are using a disconnected data context.

The issue is that deep in the bowels of Linq when calling .SubmitChanges() it does a .Equals comparison on each of the persisted fields, which fails for byte[]. This results in an "A member that is computed or generated by the database cannot be changed" error.

You may be aware of this but others thinking this is the nirvana solution and finding this entry may not be.

What is the "ultimate solution" to both problems? Still looking - but I think what Andrew Siemer proposes in terms of using a string is likely a better bet.

http://geekswithblogs.net/frankw/archive/2008/08/29/serialization-issue-with-timestamp-in-linq-to-sql.aspx

If however you are using the .Attach(modified, true) overload then byte[] will work for you. However that has other problems (such as non-optimised UPDATE statements). 10/28/2008 10:32 AM | kiwidude

# re: Serialization issue with timestamp in LINQ to SQL

You will not have serialization issues in WCF, which uses DataContractSerializer. With it, System.Data.Linq.Binary is serializable - check that. 11/8/2008 3:28 PM | Georgi Ganchev

# re: Serialization issue with timestamp in LINQ to SQL

Comment above references the wrong url for Andrew Siemer's post:
http://geekswithblogs.net/AndrewSiemer/archive/2008/02/11/converting-a-system.data.linq.binary-or-timestamp-to-a-string-and-back.aspx 2/17/2009 4:54 PM | Joshka

# re: Serialization issue with timestamp in LINQ to SQL

thanks for your post!! 4/22/2009 11:04 AM | carlos

# re: Serialization issue with timestamp in LINQ to SQL

Thanks for your very helpful article.

Such a simple solution to change hte Binary type to byte[] and this has saved me so much time and effort when i was almost thinking of giving up! 7/14/2010 6:00 AM | Jo

# re: Serialization issue with timestamp in LINQ to SQL

Thank you very much. This helped me. 12/4/2010 9:15 PM | Tushar

Post a comment