views:

68

answers:

1

Hey all,

At the moment I am experiencing a very frustrating problem. I will try to abstract the problem to make it a bit easier. It has to with serializing my custom object to a database in one process and deserializing it in another process.

I have two assemlies; AppToDB.dll and AppFromDB.dll. I have a 3rd assembly - MyCustomObject.dll - which both of these assemblies contain a reference to. The MyCustomObject.dll extends MarshalByRefObject.

In my AppToDB.dll I execute the following code:

    public bool serializeToDB(MyCustomObject obj)
    {            
        OdbcDataAdapter da = new OdbcDataAdapter();
        MemoryStream memStream = new MemoryStream();

        try
        {
            ObjRef marshalledObj = RemotingServices.Marshal((System.MarshalByRefObject)obj);

            // Serialize the object; construct the desired formatter
            IFormatter oBFormatter = new BinaryFormatter();

            // Try to serialize the object
            oBFormatter.Serialize(memStream, marshalledObj);

            // Create byte array
            byte[] serialized = memStream.ToArray();

            // Build the query to write to the database
            string queryString = 
                     "INSERT INTO MyCustomObject(id, object) VALUES(?, ?)";
            OdbcCommand command = new OdbcCommand(queryString, connection);
            command.Parameters.AddWithValue("id", 1);
            command.Parameters.AddWithValue("object", serialized);

            // Write the object byte array to the database
            int num = command.ExecuteNonQuery();
     }
     catch { }
 }

In AppFromDB.dll I execute this code:

    public OCR.Batch deserializeFromDB()
    {            
        MemoryStream memStream = new MemoryStream();

        try
        {
            string queryString = "SELECT object FROM FCBatch";
            OdbcCommand command = new OdbcCommand(queryString, connection);
            OdbcDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess);

            // Size of the BLOB buffer.
            int bufferSize = 100;
            // The BLOB byte[] buffer to be filled by GetBytes.
            byte[] outByte = new byte[bufferSize];
            // The bytes returned from GetBytes.
            long retval;
            // The starting position in the BLOB output.
            long startIndex = 0;

            MemoryStream dbStream = new MemoryStream();

            while (reader.Read())
            {
                // Reset the starting byte for the new BLOB.
                startIndex = 0;

                // Read bytes into outByte[] and retain the number of bytes returned.
                retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize);

                // Continue while there are bytes beyond the size of the buffer.
                while (retval == bufferSize)
                {
                    dbStream.Write(outByte, 0, bufferSize);
                    dbStream.Flush();

                    // Reposition start index to end of last buffer and fill buffer.
                    startIndex += bufferSize;
                    retval = reader.GetBytes(0, startIndex, outByte, 0, bufferSize);
                }

                // Write the remaining buffer.
                dbStream.Write(outByte, 0, (int)retval);
                dbStream.Flush();
            }
            // Close the reader and the connection.
            reader.Close();

            dbStream.Position = 0;
            object temp = oBFormatter.Deserialize(dbStream);
            MyCustomObject obj = (MyCustomObject)temp;

            return null;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            return null;
        }
    }

OK, so in both pieces of code you can see a MemoryStream object. In the first AppToDB it is created and if I look at its contents it contains 707 bytes. Fine. I write it to the database and save it there as a BLOB. Now in AppFromDB I retrieve the BLOB and store it in a byte[] array. I write the byte[] array to a MemoryStream again, and see that my MemoryStream objects contains 707 bytes, all of which are in place like the original. It seems I have transferred the object with success!

Now the problem lies with object temp = oBFormatter.Deserialize(dbStream);. As soon as I try to deserialize, my object is a Transparent Proxy and I am unable to cast to MyCustomObject!! How do I get my original object back? How in #@&'s name can I have a MemoryStream object....IN memory...ready to be serialized...and suddenly it's a Transparent Proxy again.

I am at loss. Help is appreciated. I will pray to #@& for the one who has the answer ;)

Edit 1 OK, I must say things are starting to make sense now (although the problem persists). My problem: I have an object (including state) on one side and I need to store it in the database so I can use it days later by another process on the other side.

My object is not serializable, because it wraps a 3rd party object which is not marked as serializable. So my only option seems to be to marshal, which returns an ObjRef, which in turn IS serializable. But of course - days later - the object I am deserializing is merely the reference and my original object is gone.

How do I solve my problem? More people must have encountered this and I just can't seem to find the answer...

Edit 2 OK, I guess I am going to write my own serializable class isomorphic to the 3rd party object. Then run through the whole 3rd party object and, store/wrap its state info etc. Then serialize my object to the database...Seems to be my only option.

Edit 3 Starting on this problem again after a while. Just realized though that solution posted in Edit 2 won't work. I have to deserialize into an object the 3rd party assembly knows, since it will continue to perform operations on it.

+1  A: 

You're serializing the ObjRef which is not the original object but rather a "remoting reference", containing all the information to transfer the reference.

From MSDN (emphasis mine):

The ObjRef contains information that describes the Type and class of the object being marshaled, its exact location, and communication-related information on how to reach the remoting subdivision where the object is located.

After a class implementing MarshalByRefObject is marshaled, the ObjRef that represents it is transferred through a channel into another application domain, possibly in another process or computer. When the ObjRef is deserialized in the target application domain, it is parsed to create a transparent proxy for the remote MBR object. This operation is known as unmarshaling.

Edit (because of to the new information provided):

There are several approaches possible to solve your problem. All of them have their own set of limitations, which I'll put together here:

  • Use XML serialization with the XmlSerializer. The XML serialization is different in that it serializes and deserializes all public properties of an object instead of serializing fields and/or custom data (when ISerializable is implemented by the object to be serialized). Therefore, if the 3rd-party objects are merely data containers, serializing the public properties is good enough and they provide a default constructor, you should be fine with this solution.

  • Build your own "low-level" serialization code, using reflection to get the fields and serialize those. This approach typically requires your application to run with full trust (reflecting and modifying private fields via reflection requires permissions which are typically not present in lower trusts). Some methods which may assist you here are the following: FormatterServices.GetSerializableMembers() to get the fields to serialize, FormatterServices.GetObjectData() to get the field data from an object instance, FormatterServices.GetSafeUninitializedObject() when deseializing to create a new, uninitialized instance (no constructor is called), and FormatterServices.PopulateObjectMembers() to write the fields back to the new instane. If you have only simple data types which are serializable in the fields, you can serialize and deserialize the object[] which you use to store the field data.

  • Your current idea, which is to manually write a replica of the 3rd-party objects. This can be very painful and basically only works when also XML serialization would work. If properties are read-only for sinatce, you'll not get far with this approach.

Lucero
Thanks for your answer. But now I am really at loss. MyCustomObject contains methods and state information - like any other object - which I have to store and retrieve at the other end. How do I do it?? I've been working on this for days now :(
Waldo Spek