Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

MarshalByRef class and non-serializable members

157 views
Skip to first unread message

alex

unread,
Apr 2, 2008, 3:24:37 AM4/2/08
to
Hello
I need to send an object of HttpListenerContext class to another AppDomain.
HttpListenerContext class is neither inheritor of MarshalByRefObject nor
marked as Serializable (more over it is sealed). I wrote a wrapper for this
class and tried to send an object of this class which inherits MarshlByRefObject
to another AppDomain:

public class web_request_parameters : MarshalByRefObject
{
private readonly HttpListenerContext context_;

public HttpListenerContext context
{
get { return this.context_; }
}

public web_request_parameters( HttpListenerContext context )
{
this.context_ = context;
}
}

Object is successfully passed through AppDomain boundaries but when I try
to access the "context" property of this class I get an Exception "Type 'System.Net.HttpListenerContext'
in Assembly 'System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
is not marked as serializable."

So my question is: should all members of the class which inherits MarshalByRefObject
be either Serializable or inherit MarshalByRefObject?
May be there are some workarounds this work?

Thank you


Jeroen Mostert

unread,
Apr 2, 2008, 2:18:21 PM4/2/08
to
alex wrote:
> I need to send an object of HttpListenerContext class to another
> AppDomain.

Why? That would be my first question. There's something very unintuitive
about one AppDomain creating this object and another one using it.

> HttpListenerContext class is neither inheritor of
> MarshalByRefObject nor marked as Serializable (more over it is sealed).
> I wrote a wrapper for this class and tried to send an object of this
> class which inherits MarshlByRefObject to another AppDomain:
>
> public class web_request_parameters : MarshalByRefObject
> {
> private readonly HttpListenerContext context_;
>
> public HttpListenerContext context
> {
> get { return this.context_; }
> }
>
> public web_request_parameters( HttpListenerContext context )
> {
> this.context_ = context;
> }
> }
>
> Object is successfully passed through AppDomain boundaries but when I
> try to access the "context" property of this class I get an Exception
> "Type 'System.Net.HttpListenerContext' in Assembly 'System,
> Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is
> not marked as serializable."
>

The reason this doesn't work is simple: while web_request_parameters is a
MarshalByRefObject (and thus lives only in its originating AppDomain) the
system still has to marshal the HttpListenerContext object itself when it's
accessed, and this of course fails for the same reason that passing it
directly fails. In other words, the wrapper isn't wrapping at all.

> So my question is: should all members of the class which inherits
> MarshalByRefObject be either Serializable or inherit MarshalByRefObject?

Not exactly.

A class which is serializable can control its own serialization by
implementing ISerializable, so it can decide to serialize its parts however
it chooses -- they needn't be serializable themselves. However, if it
doesn't do this and it relies on default serialization, then all fields not
marked [NonSerialized] must be serializable themselves. (All
MarshalByRefObjects are serializable; they serialize by passing proxies.)

For MarshalByRefObject, there's the exception that, obviously, a member need
not be marshallable if it's never accessed from another AppDomain.
Otherwise, it needs to be serializable just as if it had been used directly.
Method calls are remotable as long as their parameters and results are (and
the getters and setters of properties are just special methods).

> May be there are some workarounds this work?
>

Well, the "workaround" (I'd call it the proper approach, rather) is to keep
the HttpListenerContext in its own AppDomain, so that only that AppDomain is
responsible for it. This is why AppDomains exist in the first place: to
provide a clean separation.

That's not to say it's hopeless. You can use an MBRO as a proxy to marshal
request and results back and forth -- that is, don't try to use an
HttpListenerContext directly in a cross-domain context, instead call methods
and pass simpler types on your proxy (which in turn delegates to the
HttpListenerContext) to achieve what you want.

Working with AppDomains can be tricky. It's a good idea to exactly define
the minimal interfaces you want to communicate with. Don't simply do
cross-AppDomain calls willy-nilly, because the overhead is considerable
(it's essentially just a local form of remoting) and as you've experienced
it's easy to run into problems with nonserializable types. You also lose the
benefits of AppDomains somewhat (clean separation and unloading
capabilities) if you start migrating a lot of data across them.

--
J.

alex

unread,
Apr 3, 2008, 2:59:23 AM4/3/08
to
Hello Jeroen,

Thank you for your reply

> Why? That would be my first question. There's something very
> unintuitive about one AppDomain creating this object and another one
> using it.

There was an idea to create asp.net host (by calling ApplicationHost.CreateApplicationHost())
on the first user request to decrease startup time of the server. Thats why
HttpListener had to be created before creating of asp.net host AppDomain
and in another than this AppDomain. Then we use our custom worker_request
which inherits standard SimpleWorkerRequest (SimpleWorkerRequest doesn't
use HttpListenerContext so we need extend it with worker_request which uses
HttpListenerContext while) which is used to call HttpRuntime.ProcessRequest(worker_request).

> Not exactly.
>
> A class which is serializable can control its own serialization by
> implementing ISerializable, so it can decide to serialize its parts
> however it chooses -- they needn't be serializable themselves.
> However, if it doesn't do this and it relies on default serialization,
> then all fields not marked [NonSerialized] must be serializable
> themselves. (All MarshalByRefObjects are serializable; they serialize
> by passing proxies.)
>
> For MarshalByRefObject, there's the exception that, obviously, a
> member need not be marshallable if it's never accessed from another
> AppDomain. Otherwise, it needs to be serializable just as if it had
> been used directly. Method calls are remotable as long as their
> parameters and results are (and the getters and setters of properties
> are just special methods).

Thank you for clearing this moment

> Well, the "workaround" (I'd call it the proper approach, rather) is to
> keep the HttpListenerContext in its own AppDomain, so that only that
> AppDomain is responsible for it. This is why AppDomains exist in the
> first place: to provide a clean separation.
>
> That's not to say it's hopeless. You can use an MBRO as a proxy to
> marshal request and results back and forth -- that is, don't try to
> use an HttpListenerContext directly in a cross-domain context, instead
> call methods and pass simpler types on your proxy (which in turn
> delegates to the HttpListenerContext) to achieve what you want.
>
> Working with AppDomains can be tricky. It's a good idea to exactly
> define the minimal interfaces you want to communicate with. Don't
> simply do cross-AppDomain calls willy-nilly, because the overhead is
> considerable (it's essentially just a local form of remoting) and as
> you've experienced it's easy to run into problems with nonserializable
> types. You also lose the benefits of AppDomains somewhat (clean
> separation and unloading capabilities) if you start migrating a lot of
> data across them.

Yes idea with proxy was considered. And yes - you absolutely right that performance
considerations are very significant with proxy approach. I will check how
intensively these methods are called when request is processed.
Another idea - is to pass only neccessary data to asp.net host AppDomain
(e.g. headers, response stream, etc.) which are serializable and can be passed
and accessed in another AppDomain - but currently there is no clear point
that this will work


0 new messages