【问题标题】:Deserializing a MemoryStream - unexpected behaviour反序列化 MemoryStream - 意外行为
【发布时间】:2010-08-23 19:34:58
【问题描述】:

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

我有两个组件; AppToDB.dllAppFromDB.dll。我有第三个程序集 - 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 好的,我必须说现在事情开始变得有意义了(尽管问题仍然存在)。我的问题:我在一侧有一个对象(包括状态),我需要将它存储在数据库中,以便几天后我可以在另一侧的另一个进程中使用它。

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

如何解决我的问题?肯定有更多人遇到过这个问题,我似乎无法找到答案......

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

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

【问题讨论】:

标签: c# serialization memorystream


【解决方案1】:

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

来自 MSDN(重点是我的):

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

在一个类实现之后 MarshalByRefObject 被封送, 代表它的 ObjRef 是 通过渠道转入 另一个应用程序域,可能 在另一个进程或计算机中。什么时候 ObjRef 在 目标应用程序域,它是 解析创建透明代理 用于远程 MBR 对象。这个 操作称为解组。

编辑(由于提供的新信息):

有几种方法可以解决您的问题。它们都有自己的限制,我将在这里汇总:

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

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

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

【讨论】:

  • 感谢您的回答。但现在我真的很茫然。 MyCustomObject 包含方法和状态信息——就像任何其他对象一样——我必须在另一端存储和检索它们。我该怎么做??我已经为此工作了好几天了:(
猜你喜欢
  • 2012-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-20
  • 2011-11-22
  • 2019-01-08
  • 1970-01-01
相关资源
最近更新 更多