Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

System.Data.Linq.Binary is not XmlSerializable


So I ran into an annoying problem today with an entity that has a timestamp. When imported LINQ to SQL will create a Binary type for this the timestamp and as it turns out this type does not serialize via XML. This means ASMX Web services fail as well as plain old XmlSerializer style serialization. You’ll get:

System.Data.Linq.Binary cannot be serialized because it does not have a parameterless constructor.

Uh, how can you build a new type for a data component that doesn’t serialize natively?

The only reason I use a timestamp field in the first place is to facilitate updates with LINQ to SQL especially in some disconnected scenarios. Without time stamps many update scenarios send all the data back in the Where clause every time and some disconnected updates don’t work altogether (with invalid results) unless a timestamp is present. That’s scary enough as it is without the serialization problems.

I ran into this while working on my side project – CodePaste.net - and providing a general mechanism to retrieve content using various output mechanisms. Basically most Urls on the site can be accessed with a ?format=json/format=xml/format=rss/format=atom and retrieve that data. Json worked fine I figured xml would be a no-brainer but due to this little SNAFU the xml serialization is not working.

Workarounds

The easiest and most cases most practical solution is to make the timestamp internal:

TimeStampInternal

And this indeed does the trick. This hides the property from the XmlSerializer so serialization works properly for Web Services and plain ole’ XML Serialization.

However, this may cause some other problems as it hides the timestamp from other code that might access it. If you were using a timestamp to do possible disconnected entity updates then hiding the timestamp in this way may not allow you to pass the change status around with the entity.

Another example, and one I ran into  in my business object framework: I check for the tstamp field to detect a new value vs. an update:

if (this.TableInfo.VersionField != null)
{
    object tstamp = entity.GetType()
                          .GetProperty(this.TableInfo.VersionField)
                          .GetValue(entity, null);
    IsNew = tstamp == null;
}
else   // try to do it off the PK field
{
    object pkVal = entity.GetType().GetProperty(this.TableInfo.PkField).GetValue(entity, null);
    IsNew = this.IsPkEmpty(pkVal);
}

which now fails, because the timestamp is internal and the business framework lives in a different assembly which now no longer can see the timestamp property.

This can be fixed with with this code:

object tstamp = entity.GetType()
                      .GetProperty(this.TableInfo.VersionField,BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                      .GetValue(entity, null);

but this is problematic if you happen to run this code in a low trust environment that doesn’t support private Reflection (although it does appear to work in Medium trust for Web apps).

I tweeted this on Twitter and a number of folks suggested using [XmlIgnore] to get around the serialization issue, but since the entity is generated by LINQ to SQL designer I don’t think there’s a way to inject the [XmlIgnore] attribute. If anybody sees a better solution than the Internal switch I’d love to hear it.

Make Donation
Posted in LINQ  


Feedback for this Post

 
# re: System.Data.Linq.Binary is not XmlSerializable
by Cengiz July 17, 2009 @ 1:23am
Did you try using IXmlSerializable or ISerializable interfaces with a partial class?
# Interesting Finds: July 17, 2009
by Jason Haley July 17, 2009 @ 4:16am
Interesting Finds: July 17, 2009
# re: System.Data.Linq.Binary is not XmlSerializable
by Justin B July 17, 2009 @ 8:00am
Not sure if this helps..but you might be able to easily swap to entity framework and the classes generate from there should serialize for
# re: System.Data.Linq.Binary is not XmlSerializable
by David DeWinter July 17, 2009 @ 8:08am
You can serialize the Binary class to XML using the DataContractSerializer or use Cengiz's suggestion to implement IXmlSerializable on a partial class.

Also, if you set your environment to low trust the complexity of queries you can do with LINQ-based providers drops significantly, so I think your point that "this is problematic if you happen to run this code in a low trust environment that doesn’t support private Reflection" is a non-issue.
# re: System.Data.Linq.Binary is not XmlSerializable
by Rick Strahl July 17, 2009 @ 10:57am
@David - could you clarify by what you mean by "low trust the complexity of queries you can do with LINQ-based providers drops significantly"? What are you running into there? Expression tree creation failing due to permissions? Asking cause I haven't seen it, although frankly I rarely run really low trust environments. If you have some thoughts on this I'd love to hear it.
# System.Data.Linq.Binary is not XmlSerializable
by DotNetKicks.com July 17, 2009 @ 10:58am
You've been kicked (a good thing) - Trackback from DotNetKicks.com
# re: System.Data.Linq.Binary is not XmlSerializable
by Jason Jarrett July 17, 2009 @ 1:13pm
Is the linq to sql Designer a requirement for your project(s)? Could you use a T4 template instead? I know there a number of L2S T4 generation templates out there. This would also give you the flexibility to apply this to all entities with a TimeStamp type. (and tweak any other L2S code generation you'd like)
# re: System.Data.Linq.Binary is not XmlSerializable
by David DeWinter July 17, 2009 @ 2:04pm
@Rick consider the following:

public class CustomerRepository
{
// Fields, constructors...

public Customer GetCustomerById(int customerId)
{
return this._context.Customers.FirstOrDefault(c => c.CustomerId == customerId);
}

}

This actually won't work in low trust environments (specifically, environments that do not grant ReflectionPermission with RestrictedMemberAccess). Since customerId is accessed in the expression tree through a closure, the compiler generates a private nested class within the CustomerRepository that has a single int field to represent the customerId. When the expression tree is translated, LINQ to SQL has to get the value of the field through reflection into the private class.

So if you're running in low trust you won't be able to do simple scenarios like this because of the reflection requirement. Sharepoint 2007's medium trust level doesn't support RestrictedMemberAccess out of the box, so you'll run into an Exception which I mention here: http://blogs.rev-net.com/ddewinter/2009/04/22/using-linq-to-sql-and-ef-in-sharepoint-under-medium-trust/
# re: System.Data.Linq.Binary is not XmlSerializable
by Cengiz July 19, 2009 @ 2:08pm
I think the other solutions is more painless but there is another option :"extern alias"
maybe using this method can help you.

http://blogs.msdn.com/abhinaba/archive/2005/11/30/498278.aspx
# re: System.Data.Linq.Binary is not XmlSerializable
by Scott Mitchell August 03, 2009 @ 10:49am
Rick, why not create a Data Transfer Object (DTO) that contains the precise state you need to return via the Web Service? The Web Service would then get the L2S object from the repository, create a synonymous DTO, and then return that DTO.

This L2S-to-DTO mapping can be done in two lines of code with the AutoMapper library:
http://www.codeplex.com/AutoMapper
# re: System.Data.Linq.Binary is not XmlSerializable
by Rick Strahl August 03, 2009 @ 1:51pm
@Scott - that works, but seriously this is overkill for something like a generic data export mechanism. Especially so since L2S actually is support to generate the appropriate tags to use the entities as serializable instances.

FWIW, DTOs are fine in true service scenarios but for AJAX callbacks and 'generic' data results it's really not what useful to create a separate set of DTOs that match the structure of the original object exactly. What's the point other than busy work and CPU cycles to copy data.
# re: System.Data.Linq.Binary is not XmlSerializable
by Gooner85 May 30, 2011 @ 8:47am
Frank Wang has a simple solution. Just change the datatype from Binary to Byte[]

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


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014