Friday, April 3, 2009

LINQ to SQL can't be serialized when using Windows Workflow Foundation (WF), but there is a solution.

OK, the statement is not strictly true. However, it is a lot more difficult than it needs to be. In my opinion the serialization for LINQ to SQL was poorly designed.

The first question might be, how do you know that? I don't really see an error in my workflow. One way is that the WorkflowTerminated event in Windows Workflow (WF) fires when your workflow runs. It should not fire under typical circumstances. If you look at the WorkflowTerminatedEventArgs parameter you will find your Exception hidden in layers of inner exceptions. Since, workflows are executed in an asynchronous manner (ASP.NET is, but threading is different), the line of code after your start workflow will not be stopped because the current thread didn't throw an exception. The thread where the workflow ran thew the exception (assumign that it wasn't already handled), and shows in the WorkflowTerminated event.

You will eventually find that you get erors like the following:

{"Type 'System.Data.Linq.ChangeTracker+StandardChangeTracker' in Assembly 'System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable."}

or

EventArgs can't be serialized

or

other serialization error indicating that the object that you are passing to the workflow can't be serialized.

If you don't care about the road to misery of why LINQ to SQL is not the solution, just skip to the conclusion section.

To better understand what is going on, let's first look at what kind of serialization LINQ to SQL does support. It supports DataContract Serialization which is Unidirectional serialization. By default, when using SQLMetal or Visual Studio 2008 (VS2008) Object Relational Designer (O/R Designer) serialization is set to None. To support DataContract Serialization, you need to set the serialization property to Unidirectional. This will change the code generated to have DataContract and DataMember attributes generated. These attributes are used by the DataContractSerializer. I believe this is also the serialization used in Windows Communication Framework (WCF). For more information on this, check out
http://msdn.microsoft.com/en-us/library/bb546184.aspx .

There are also some issues with circular reference, but Unidirectional serialization is supposed to address this.
Here is a framework that attempts to address the issue.

There are other types of serialization that .Net supports such as XmlSerialization and BinarySerialization. BTW, viewstate doesn't use any of these. It has its own type of serialization called
LosFormatter . Be careful trying to put a LINQ to SQL entity into the viewstate or the session if you use a session store. Particularly if it is large. See here for more details. For more information on the different types of serialization check out: http://msdn.microsoft.com/en-us/library/cc656732.aspx .

In Windows Workflow (WF) the Persistence Service is responsible for serializing and deserializing objects that are passed to the workflow. Assuming you are using the out of the box, SqlWorkflowPersistenceService it uses the BinarySerialization. The problem is that LINQ to SQL does NOT support BinarySerialization due to the very important EntityRefs that it keeps.

This in my opinion is where the poor design is. Had they taken an approach much more like they did with
ADO.NET Entity Framework , Binary Serialization would work just fine. Why they designed a framework such as LINQ to SQL that is so incompatible with WF, I can't imagine. It seems like the two products were developed in silos. It totally get that the WF workflow needs to have binary serialization to support all types of data, I just don't get why LINQ to SQL was not designed with that in mind. I would love for someone to explain to me the situation.

If you are like me, you REALLY like LINQ. It has so many benefits like working for any type of data source (or at least can be extended to do so). However, I believe the LINQ to SQL version of LINQ has HUGE issues when it comes to Serialization. There are some work arounds thankfully. Unfortunately, none of them are perfect.

This blog discusses one technique, but was not sufficient because it didn't address WF incompatibility.

This blog really summarizes the issues and the work arounds fairly well if you read through all the links in the blog. If you read all the links, you should have a pretty good understanding of the issue. In my opinion, the only one that works is solution usedby the Layer Sample . The problem is that I think for many smaller applications the architecture is overkill. And more importantly, it is difficult to find the exact code to extract from this project since it has so many pieces. Read the last entry on this page by Firedancer on May 29, 2008. This solution is taken from the Layer Sample. All you have to due is clone each object you want to pass to the WF workflow. Unfortunatly, any relationships between the objects will be lost, and you will have to reconstruct it later. This is the problem I have with this solution. I want to be able to pass the object or even objects if I want as a parameter to the WF workflow and have it be the same when I am working in the WF workflow. In particular, I want all the related objects (Entities) to come along also. But, the very part of the workaround that allows it to work, creates this problem. So close, but still not there.

I just could not believe there was no good solution out there and from Microsoft. I don't want to install any third party solution. I figured there must be something I am missing. The answer is ADO.NET Entity Framework that I mentioned earlier. I provides exactly what I want, and adds to it the ability to synchronize the mapping of object model and database, and the option to have a conceptual model instead of having to map directly to the database as my model. The best part is that it is compatible with BinarySerialization and thus WF! Oh, and did I forget you can query the Object Model from ADO.NET Entity Framework using LINQ! What more could I ask for.

The conclusion
My strong recommendation is do NOT use the VS2008 LINQ to SQL file type or LINQ to SQL when using WF. It is just a major headache. Instead use ADO.NET Entity Framework. You can use LINQ on it, so you get the best of both worlds.

To summarize the benefits of using LINQ to Entities and ADO.NET Entity Framework.
Reference
  • You can properly map the database to an object model.
  • You get a conceptual model that allows your objects to be different that your tables.
  • Supports Binary Serialization (including related entities).
  • Compatible with Windows Workflow Foundation (WF)
  • Can query object model using Entity SQL or LINQ to Entities.
  • Relationship navigation between entities that is serialized.
  • Object graph of objects loaded into memory are passed when one of the objects of the graph are passed around. This means no container needed to pass around an object graph like you do with a DataSet.


Additional Resources

No comments: