反序列化MemoryStream - 意外行为

时间:2010-06-23 09:35:15

标签: c# serialization memorystream

目前我遇到了一个非常令人沮丧的问题。我将尝试抽象问题,使其更容易一些。它必须在一个进程中将我的自定义对象序列化到数据库,并在另一个进程中反序列化它。

我有两个装置; AppToDB.dllAppFromDB.dll。我有一个第3个程序集 - MyCustomObject.dll - 这两个程序集都包含对它的引用。 MyCustomObject.dll扩展MarshalByRefObject

在我的AppToDB.dll中,我执行以下代码:

    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 { }
 }

AppFromDB.dll我执行此代码:

    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;
        }
    }

好的,所以在这两段代码中你都可以看到一个MemoryStream对象。在第一个AppToDB中创建它,如果我查看它的内容,它包含707个字节。精细。我将它写入数据库并将其保存为BLOB。现在在AppFromDB我检索BLOB并将其存储在byte[]数组中。我再次将byte[]数组写入MemoryStream,并看到我的MemoryStream个对象包含707个字节,所有这些都与原始对象一样。看来我已经成功转移了这个对象!

现在问题在于object temp = oBFormatter.Deserialize(dbStream);。一旦我尝试反序列化,我的object是透明代理,我无法转换为MyCustomObject!如何取回原始物体? #@&的名字怎么能有一个MemoryStream对象....在内存中......准备好被序列化......突然间它又是一个透明代理。

我很茫然。感谢帮助。我会祈祷#@&对于有答案的人;)

修改1 好吧,我必须说事情现在开始变得有意义了(尽管问题仍然存在)。我的问题:我有一个对象(包括状态),我需要将它存储在数据库中,所以我可以在几天之后通过另一个进程使用它。

我的对象不可序列化,因为它包装了一个未标记为可序列化的第三方对象。因此,我唯一的选择似乎是编组,它返回一个ObjRef,而后者又是可序列化的。但是当然 - 几天之后 - 我反序列化的对象仅仅是引用而我的原始对象已经消失了。

如何解决我的问题?更多的人一定遇到过这种情况,我似乎无法找到答案......

编辑2 好吧,我想我打算将自己的可序列化类同构为第三方对象。然后运行整个第三方对象,并存储/包装其状态信息等。然后将我的对象序列化到数据库......似乎是我唯一的选择。

编辑3 一段时间后再次开始这个问题。刚刚意识到,在Edit 2中发布的解决方案将无效。我必须反序列化为第三方程序集所知道的对象,因为它将继续对其执行操作。

1 个答案:

答案 0 :(得分:1)

您正在序列化ObjRef,它不是原始对象,而是“远程引用”,包含传输引用的所有信息。

来自MSDN(强调我的):

  

ObjRef包含的信息   描述了类型和类   对象被编组,确切地说   位置和与通信有关   有关如何到达的信息   远程细分对象所在的位置   位于。

     

课程实施后   MarshalByRefObject被编组了   代表它的ObjRef是   通过渠道转移到   另一个应用领域,可能   在另一个过程或计算机中。什么时候   ObjRef在中被反序列化   目标应用程序域,它是   解析以创建透明代理   用于远程MBR对象。这个   手术被称为解组。

修改(由于提供了新信息):

有几种方法可以解决您的问题。所有这些都有各自的局限性,我将在这里加以说明:

  • XmlSerializer使用XML序列化。 XML序列化的不同之处在于它序列化和反序列化对象的所有公共属性,而不是序列化字段和/或自定义数据(当要序列化的对象实现ISerializable时)。因此,如果第三方对象仅仅是数据容器,那么序列化公共属性就足够了并且它们提供了一个默认构造函数,你应该对这个解决方案没问题。

  • 构建您自己的“低级”序列化代码,使用反射来获取字段并对其进行序列化。此方法通常要求您的应用程序以完全信任的方式运行(通过反射来反映和修改私有字段需要通常在较低信任中不存在的权限)。可以在此处为您提供帮助的一些方法如下:FormatterServices.GetSerializableMembers()以获取要序列化的字段,FormatterServices.GetObjectData()以从对象实例获取字段数据,FormatterServices.GetSafeUninitializedObject()在取消初始化以创建新的时,未初始化的实例(没有构造函数被调用),FormatterServices.PopulateObjectMembers()将字段写回新的instane。如果您只有字段中可序列化的简单数据类型,则可以序列化和反序列化用于存储字段数据的object[]

  • 您当前的想法,即手动编写第三方对象的副本。这可能非常痛苦,并且基本上只有在XML序列化工作时才有效。如果属性对于sinatce是只读的,那么这种方法就不会有太大作用。