The new information regarding MemoryStream confuses me a little bit...
Ok, maybe it worth a bit of explaining the concepts of .NET remoting. There are two ways of
marshalling .NET objects across remoting borders: marshalling by reference and marshalling by value.
Classes marshalled by value are marked with Serializable attribute. Their instances are turned into bytes,
sent across the wire and re-instantiated on the other side of your channel. When you make a remote call,
you get a fresh copy of such an object on the remote side.
Classes marshalled by reference are derived from MarshalByRefObject class. Their instances are not copied
when you try to send them across the wire. When you make a remote call, the remote side gets a proxy
which allows calling methods of your object existing on your local side.
Now, have a look yourself at Stream class source code:
MemoryStream is derived from Stream, and Stream is derived from MarshalByRefObject. So, MemoryStream
is not serializable by definition. When you pass an instance of MemoryStream to you remote server, it is not
just copied there. It retains on the client side, where the server tries to access it using a proxy.
Some time ago, we wrote a serialization helper which should allow us to serialize datasets and datatables
concurrently. This helper serialized the datatables into multiple MemoryStream which then were serialized
into a single MemoryStream which could be saved into a file.
The ability to copy a stream to a file has nothing to do with Serializable concept. Serializable means
that an object is copied across the wire, MarshalByRefObject means that the object is not copied,
and the remote side just gets a proxy of it.
What also confuses me: If the framework awaits all methods of the proxy to return, then why was the
container disposed before the receiving proxy was able to read the object from the container?
At this point you probably can answer this question yourself. The server doesn't own your MemoryStream,
it owns nothing but a remote proxy of it. So it doesn't manage the lifetime of the stream and cannot use it
freely any time it wants. If by the moment your server reaches back your local memory stream it's already
disposed of on the local side (which can happen after your call), ObjectDisposedException is thrown.
the byte implementation was our original one, I tried to get away from it since we're actually serializing
our objects into a stream, copying this stream into a byte and then serializing the byte again... quite
some overhead regarding memory consumption as well as performance.
Did you really measure it? You actually add much-much more overhead with your MemoryStream
implementation, even if you find a way to overcome an ObjectDisposedException by holding on it a while.
You make your server accessing the memory stream remotely, which is painfully slow compared to sending
an array of bytes in a single roundtrip.