I hope everyone’s a bit familiar with .NET remoting because I think I’ve confused myself. J  This post talks about the behavior of ByRef (VB.NET) and ref (C#) keywords for remoting calls. 

 

Let’s say I have a remoting object that exposes the following method and I would like to call it from a remote client.

 

              public void PassDataSet(ref System.Data.DataSet ds)

              {

                     ds.Tables.Add();

              }

 

Does this even work?  Why, yes, yes it does!  And even better, does the remote client see the changes that were made on the server side…it sure does.  Pretty crazy hunh!  “What the heck is going on here?” you may ask.  How is it possible to pass pointer to an object to a remote server? 

 

Well, this isn’t what’s happening.  When you specify ref or ByRef on a remote function the .net framework actually serializes your parameter and adds it to the remoting message.  So, in this instance when I call PassDataSet the data set is serialized on the client, added to the remoting message and passed to the server.  The server then deseralizes the remoting message, including the dataset.  The code on the server then adds a table to the dataset, creates the return remoting message, serializes the updated dataset, and adds it to the return message.  Then, on the client side the framework deserializes the message and dataset and then updates your copy of the dataset with the version returned in the remoting message.

 

See the SOAP message of this call:

 

POST /RemoteObject.rem HTTP/1.1

User-Agent: Mozilla/4.0+(compatible; MSIE 6.0; Windows 5.1.2600.0; MS .NET Remoting; MS .NET CLR 1.1.4322.2032 )

Content-Type: text/xml; charset="utf-8"

SOAPAction: "http://schemas.microsoft.com/clr/nsassem/RemoteObjectNamespace.RemoteObject/RemoteObject#PassDataSet"

Content-Length: 1516

Expect: 100-continue

Connection: Keep-Alive

Host: localhost:8888

 

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<SOAP-ENV:Body>

<i2:PassDataSet id="ref-1" xmlns:i2="http://schemas.microsoft.com/clr/nsassem/RemoteObjectNamespace.RemoteObject/RemoteObject">

<ds href="#ref-4"/>

</i2:PassDataSet>

<a1:DataSet id="ref-4" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/System.Data/System.Data%2C%20Version%3D1.0.5000.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Db77a5c561934e089">

<XmlSchema id="ref-5">&#60;?xml version=&#34;1.0&#34; encoding=&#34;utf-16&#34;?&#62;

&#60;xs:schema id=&#34;emptydataset&#34; xmlns=&#34;&#34; xmlns:xs=&#34;http://www.w3.org/2001/XMLSchema&#34; xmlns:msdata=&#34;urn:schemas-microsoft-com:xml-msdata&#34;&#62;

  &#60;xs:element name=&#34;emptydataset&#34; msdata:IsDataSet=&#34;true&#34;&#62;

    &#60;xs:complexType&#62;

      &#60;xs:choice maxOccurs=&#34;unbounded&#34; /&#62;

    &#60;/xs:complexType&#62;

  &#60;/xs:element&#62;

&#60;/xs:schema&#62;</XmlSchema>

<XmlDiffGram id="ref-6">&#60;diffgr:diffgram xmlns:msdata=&#34;urn:schemas-microsoft-com:xml-msdata&#34; xmlns:diffgr=&#34;urn:schemas-microsoft-com:xml-diffgram-v1&#34; /&#62;</XmlDiffGram>

</a1:DataSet>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

 

And the return message from the server (note the added table to the dataset): 

 

HTTP/1.1 100 Continue

 

HTTP/1.1 200 OK

Content-Type: text/xml; charset="utf-8"

Server: MS .NET Remoting, MS .NET CLR 1.1.4322.2032

Content-Length: 1734

 

<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<SOAP-ENV:Body>

<i2:PassDataSetResponse id="ref-1" xmlns:i2="http://schemas.microsoft.com/clr/nsassem/RemoteObjectNamespace.RemoteObject/RemoteObject">

<return>1</return>

<ds href="#ref-4"/>

</i2:PassDataSetResponse>

<a1:DataSet id="ref-4" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/System.Data/System.Data%2C%20Version%3D1.0.5000.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Db77a5c561934e089">

<XmlSchema id="ref-5">&#60;?xml version=&#34;1.0&#34; encoding=&#34;utf-16&#34;?&#62;

&#60;xs:schema id=&#34;emptydataset&#34; xmlns=&#34;&#34; xmlns:xs=&#34;http://www.w3.org/2001/XMLSchema&#34; xmlns:msdata=&#34;urn:schemas-microsoft-com:xml-msdata&#34;&#62;

  &#60;xs:element name=&#34;emptydataset&#34; msdata:IsDataSet=&#34;true&#34;&#62;

    &#60;xs:complexType&#62;

      &#60;xs:choice maxOccurs=&#34;unbounded&#34;&#62;

        &#60;xs:element name=&#34;Table1&#34;&#62;

          &#60;xs:complexType&#62;

          &#60;/xs:complexType&#62;

        &#60;/xs:element&#62;

      &#60;/xs:choice&#62;

    &#60;/xs:complexType&#62;

  &#60;/xs:element&#62;

&#60;/xs:schema&#62;</XmlSchema>

<XmlDiffGram id="ref-6">&#60;diffgr:diffgram xmlns:msdata=&#34;urn:schemas-microsoft-com:xml-msdata&#34; xmlns:diffgr=&#34;urn:schemas-microsoft-com:xml-diffgram-v1&#34; /&#62;</XmlDiffGram>

</a1:DataSet>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

 

If you modify the method definition so that the dataset is not passed “by reference” the incoming remoting message will remain the same.  The return message from the server will not have the <a1:DataSet…></a1:DataSet> element.

 

As you can imagine, this could severely impact your applications performance depending on the size of your dataset.  There’s a KB article that talks about improving dataset Serialization that may help here:  Q892740 Improving DataSet Serialization and Remoting Performance.  However, if you’re not passing in a DataSet but rather a complex object type this won’t help.  What to do?  The best thing would to create your own custom serializer or only serialize those properties of your object that will be modified on the other side of your remoting boundarie.

 

One quick note:  This marshaling behavior works for the DataSet since it is marked as serializable.  If the parameter you are trying to pass as “ref” or “byref” isn’t serializeable you’ll get the following exception:

 

An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll

 

Additional information: The type RemoteObjectNamespace.RemoteThrowException in Assembly RemoteObject, Version=1.0.1851.26880, Culture=neutral, PublicKeyToken=null is not marked as serializable.

 

 

So, where does that leave us?  If you truly want to marshal an object across remoting boundaries via reference your object has to inherit from MarshalByRefObject or implement the necessary interfaces.  If that isn’t an option you have to make some performance tradeoffs.  It’s all in a days work for you architects!