Web Services - Binary Serialization instead of XML Serialization

Posts   
 
    
Jaz
User
Posts: 31
Joined: 24-Feb-2006
# Posted on: 20-Jul-2006 10:52:21   

Greetings,

I'd like to hear an oppinion of other people regarding the following idea. First some background info:

I've been involved into a development of a smart client application which is designated for two distant offices, one in USA, the other one in Europe. The application's purpose is entering and processing of medical record data. We've been developing it in .NET 2.0, SQL server 2005 and LLBLGen Pro v1.0.2005.1. Both client and server are .NET and will be using the same LLBLGen generated object model. I know that in a trusted environment like this, a remoting is suggested (I've read it many times on this forum), but when we needed to make this main architectural decision 6 months ago, due to our client's various firewall problems and some other reasons, we decided we should use web services. Now, I know that this may have been a wrong decision to begin with, but we aren't gonna change it now. Anyway, that's the situation, we are fetching and saving LLBLGen entity objects and collections via web services and we are using the adapter templates with web service extensions, so the LLBLGen objects are generated fine in the proxy classes.

Recently, an idea occured to me - why not just use an xml serialization wrapper for binary serialized objects. The code is fairly simple:


[XmlSchemaProvider("GetSchema")]
    public class XmlSerializableWrapperForBinarySerializable: IXmlSerializable
    {
        const string ns = "http://company-name/xml/serialization";

        static readonly string name = String.Format("{0}-{1}", "company-namespace", "XmlSerializableWrapperForBinarySerializable");

        public XmlSerializableWrapperForBinarySerializable()
        {
        }

        public XmlSerializableWrapperForBinarySerializable(object binarySerializableObject)
        {
            _binarySerializableObject = binarySerializableObject;
        }

        private object _binarySerializableObject;
        public object BinarySerializableObject
        {
            get { return _binarySerializableObject; }
            set { _binarySerializableObject = value; }
        }

        void IXmlSerializable.WriteXml(XmlWriter w)
        {
            w.WriteStartElement(name, ns);
            string serializedString = _binarySerializableObject != null ? 
                Convert.ToBase64String(BinarySerializer.Serialize(_binarySerializableObject)) : String.Empty;
            w.WriteElementString("binarySerializableObject", ns, serializedString);
            w.WriteFullEndElement();
        }

        void IXmlSerializable.ReadXml(XmlReader r)
        {
            bool hasWrapper = false;
            if (r.LocalName != name)
            {
                hasWrapper = true;
                //read wrapper element
                r.ReadStartElement();
            }
            //read written root element
            r.ReadStartElement(name, ns);

            string serializedString = r.ReadElementString("binarySerializableObject", ns);
            if (!String.IsNullOrEmpty(serializedString))
            {
                _binarySerializableObject = BinarySerializer.Deserialize(Convert.FromBase64String(serializedString));
            }

            //close written root element
            r.ReadEndElement();
            if (hasWrapper)
            {
                //close wrapper element
                r.ReadEndElement();
            }
        }

        XmlSchema IXmlSerializable.GetSchema() { return null; }

        public static XmlQualifiedName GetSchema(XmlSchemaSet xss)
        {
            XmlSchema xs = XmlSchema.Read(
                new StringReader(
                    String.Format(
                        @"<xs:schema id='{1}-Schema' targetNamespace='{0}' elementFormDefault='qualified' xmlns='{0}' xmlns:mstns='{0}' xmlns:xs='http://www.w3.org/2001/XMLSchema'><xs:complexType name='{1}'></xs:complexType></xs:schema>",
                        ns,
                        name
                    )
                ),
                null
            );
            xss.XmlResolver = new XmlUrlResolver();
            xss.Add(xs);
            return new XmlQualifiedName(name, ns);
        }

Mind that using a class like this requires writing an own SchemaImporterExtension, so it can be properly generated in the web service proxy classes. For the people that don't know what I'm talking about, just browse the LLBLGen's forums or even better, go to the customer section, there you can find a word document that describes the whole process in detail.

And now to the main discussion. I see the following advantages when using the class above:

  1. Binary serialization of LLBLGen's objects or any other object is faster then XML serialization (I know that class above includes the XML serialization as well, but that's just the byte array to string conversion, it should be fast enough).

  2. Data transmitted over the wire should be smaller (not sure about this one, it should be tested)

  3. This is actually a side effect, but it may come handy - data is crypted, although in a primitive way. Still, one could implelement a proper encryption in the wrapper's Serialize and Deserialize methods.

  4. Nasty web services time zone bug is eliminated. For the people that are unaware of this - Xml serialization always looks at the local time zone in registry (it can't be changed like current CultureInfo) when serializing & deserializing datetime structure. It always appends the timezone data with it. This may be desired in some cases, but mostly it is not. As I mentioned above, our users are entering the medical record data with the exact time of the surgery. Now, if surgery happened at 2:05 in USA, and if a person from Europe is entering it, It will be recorded to the database as 9:05, there will be a 7 hours time difference. You can find a lot of info about this problem on the internet, so I won't go any further.

  5. This one is the best - UnitOfWork can be sent to the web service method. Now, for all of your that have already used this brilliant LLBLGen's class, I don't need to waste words on how usefull it is. The ability to send the exact sequence of database actions and do it in a strongly type way is simply fantastic. Thank you Frans.

As for the disadvantages, currently I see none. Statements 1. and 2. probably need to be tested further, before one can be sure they are correct.

I have to say that I implemented this already in my application and I'm quite happy how it works. The sole benefit of being able to use the UnitOfWork was worth the trouble.

That's it, I'm eager to hear everyone's oppinion about this. Thanks in advance.

*** EDITED ***

Sorry, I forgot to explain this, In the class above you can see the BinarySerializer class. That's just my own wrapper for BinaryFormatter, for nicer looking methods that are working directly with byte array and not with the stream. Everything should work with the BinaryFormatter, or if anyone wants it, I can send the whole BinarySerializer class.


Regards,

Jaz