反序列化 MemoryStream - 意外行为

发布于 2024-09-06 20:41:41 字数 4765 浏览 6 评论 0原文

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

我有两个程序集; 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);。一旦我尝试反序列化,我的对象就是一个透明代理,我无法转换为MyCustomObject!如何取回原来的对象?我如何以 #@& 的名义拥有一个 MemoryStream 对象......在内存中......准备好序列化......突然它又变成了透明代理。

我不知所措。感谢帮助。我会向#@&祈祷对于有答案的人;)

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

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

我该如何解决我的问题?一定有更多人遇到过这个问题,但我似乎找不到答案......

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

段念尘 2024-09-13 20:41:41

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

来自 MSDN(重点是我的):

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

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

编辑(因为提供了新信息):

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

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

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文