views:

619

answers:

2

Hello, I'm having a difficult time finding information on how to elegantly serialize ActiveRecord objects.

We would like to use XML as the format because we need to output our objects in such a way that another program will be able to feasibly parse them.

XML-Serialization is usually very easy and straightforward to implement, but the problem comes when trying to serialize an object returned from the ActiveRecord database. The database returns a proxy class of the object, the type of which cannot be explicitly anticipated via the [XmlInclude] attribute.

For example:

public class Foo : ActiveRecordLinqBase<Foo>
{
   public virtual string Bar{get;set;}

   public virtual int FooId{get;set;}

   public Foo(string bar)
   {
      Bar = bar;
   }

   public static void FooSerializeExample()
   {
      Foo tehFoozor = new Foo("omgFoo!");
      tehFoozor.SaveAndFlush();
      int id = tehFoozor.FooId;

      //...
      //Assume new ActiveRecord session.

      XmlSerializer serializer = new XmlSerializer(typeof(Foo));

      Foo tehFoozorToSerialize = Foo.Find(id);

      using(Stream stream = File.OpenWrite("tehFoozor.xml"))
      {
         serializer.Serialize(stream, tehFoozorToSerialize); //Will fail
      }
   }
}

When serializing here, we'll get a message:
"The type FooProxy2e2de24df9be42909d13a67fdb00b981 was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."

where the Proxy type will be completely unpredictable (at least to my knowledge).

As a temporary solution, my team has stubbed out each AR object's properties into interfaces. Then, we implemented "Container" objects for each, which are essentially Xml-Serializable non-AR versions of the objects. Considering the fact that we currently have 18 different AR objects which are serialized, that's 36 additional files in our solution! Something (everything) tells me this is a bad solution, but I've not been able to find a better way.

We also tried using the Soap Formatter, but since ActiveRecrodLinqBase<> isn't "marked as serializable", this was a dead end also.

+1  A: 

Fetch everything you need eagerly, then use AutoMapper to map it to DTOs and serialize those DTOs.

The proxy error you're getting suggests that your DTOs are actually using NHibernate's proxied collections, when they should be using plain List<T> or arrays instead. Make sure you use ToList() or similar.

Also, you don't need to use interfaces to map DTOs.

Mauricio Scheffer
I agree.. Never try to send the actual objects over the wire.Ayende wrote about this once and I agree with him: http://ayende.com/Blog/archive/2009/05/14/the-stripper-pattern.aspx
Tigraine
I could be mistaken, but I believe this is what we're already doing: mapping (not using AutoMapper because it failed to simplify the mapping process in this situation) the objects to basic clones of the object, then serializing them. As I mentioned before, this adds 36 files to our solution, and that doesn't include unit test files.@Tigraine: We're not sending the actual objects over the wire, but we need to load parts of each object when obtaining the data we WILL send over the wire. This is why we need lazy-loading.
Anj
You're right, you're already using DTOs but it seems that the DTO mapping is not quite right. See my updated answer.
Mauricio Scheffer
The problem does not rely on the type of the collections in the objects. I have used the [XmlIgnore] to ignore each of the actual collections, and used [XmlArray("<PropertyName>")] and [XmlArrayItem("<ArrayItemType>")] on a separate wrapper property which converts the collection to/from a serializable array. The current solution maps to/from the objects and serializes just fine, but it's not the elegant solution we desire.One of the senior developers is looking into using a custom XmlSerialization solution which uses deep reflection.
Anj
There's no need for xml attributes or custom serializations. You only need to make sure that the DTOs have no proxies and are plain lists/arrays. Check everywhere where a proxy could be used. If it's not collections, check the lazy [BelongsTo]
Mauricio Scheffer
I do appreciate your input, but I believe this discussion is answering the wrong question. Our current solution, that is mapping to DTOs and serializing/deserializing them, logically works and has no problems. However, we are looking for a more elegant solution which does not involve creating a DTO for each object we want to serialize.
Anj
Oh, ok, sorry for the misunderstanding
Mauricio Scheffer
A: 

Try the DataContractSerializer.

Its equivalent of [XmlInclude] is [KnownType], which includes a version that can dynamically supply the type to include the first time the type is reflected. See http://msdn.microsoft.com/en-us/library/ms730167.aspx

Also, I think (though not 100% sure) that .NET4.0 will include a "Type Mapper" feature in the DataContractSerializer which specifically makes scenarios like this easier.

If you do end up trying the DCS and will run into questions, post as comments to this answer and I'll try to answer them.

Eugene Osovetsky
Will test to see if this solution is viable, and update here the results. Thanks!
Anj