Contact   •   Articles   •   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  

The Voices of Reason


 

Cengiz
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

Did you try using IXmlSerializable or ISerializable interfaces with a partial class?

Jason Haley
July 17, 2009

# Interesting Finds: July 17, 2009

Interesting Finds: July 17, 2009

Justin B
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

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

David DeWinter
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

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.

Rick Strahl
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

@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.

DotNetKicks.com
July 17, 2009

# System.Data.Linq.Binary is not XmlSerializable

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Jason Jarrett
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

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)

David DeWinter
July 17, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

@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/

Cengiz
July 19, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

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

Scott Mitchell
August 03, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

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

Rick Strahl
August 03, 2009

# re: System.Data.Linq.Binary is not XmlSerializable

@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.

Gooner85
May 30, 2011

# re: System.Data.Linq.Binary is not XmlSerializable

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 - 2015