反序列化 MemoryStream - 意外行为
目前我遇到了一个非常令人沮丧的问题。我会尝试将问题抽象化,使其变得更容易一些。它必须在一个进程中将我的自定义对象序列化到数据库,然后在另一个进程中将其反序列化。
我有两个程序集; AppToDB.dll
和 AppFromDB.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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您正在序列化
ObjRef
这不是原始对象,而是一个“远程引用”,包含传输引用的所有信息。来自 MSDN(重点是我的):
编辑(因为提供了新信息):
有多种方法可以解决您的问题。它们都有自己的一组限制,我将在这里汇总:
将 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):
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 (whenISerializable
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.