Welcome to MSDN Blogs Sign in | Join | Help

I have seen numerous blog and newsgroup posts about how XmlSerialization and ASMX WebServices serialize DateTime in v1.0 and v1.1 of the framework.  Well, I have some good news, bad news and good bad news for you.

 

Good news:  We fixed it.

Bad news: If you worked around it then you might be broken on upgrade to v2.0

Good of the bad news:  Don’t worry there is a workaround to get the old broken behavior back.

 

In case you missed it, here is a quick summary of the problem:

 

In v1.0 and v1.1 of the framework when serializing DateTime values with the XmlSerializer, we would always append the local timezone of the machine. 

 

There are several problems with that:

1) DateTime doesn’t have a notion of timezone so why would we append it

2) Since DateTime didn’t have a timezone indicator a DateTime intended to represent UTC would come up with the local timezone on it

3) When reading a DateTime off the wire, we would automatically adjust the time based on the timezone offset relative to the local timezone on the receiving machine

 

Needless to say, this caused some problems for some folks. 

 

How we fixed it

 

v2.0 now has a notion of DateTimeKind to help developers indicate the nature of the DateTime.  It can be Local, UTC or Unspecified.

 

Using this value in XmlSerialization and System.Xml.XmlConvert allows us to now de/serialize DateTime and preserve its desired timezone. 

 

Here is a little table to show you the differences:

 

 

DateTime.Now

new DateTime()

DateTime.UtcNow

v1.0 and v1.1 output

2004-10-13T15:16:11.5427356-07:00

1999-02-02T12:24:48.0000000-08:00

2004-10-13T22:16:11.5427356-07:00

DateTimeKind on v2.0

Local

Unspecified

UTC

v2.0 output

2004-10-13T15:16:11.5427356-07:00

1999-02-02T12:24:48.0000000

2004-10-13T22:16:11.5427356Z

 

In order to get this new behavior you only need to upgrade to v2.0 of the .NET Framework.

 

How you could be broken on upgrade

 

The reason I am telling you this is that you might have code that works around this problem today and upgrading will change the date representation on the wire and could break your existing applications.

 

Some interesting things to note:

-          DateTime comparison functions ignore the new DateTimeKind field

o        Even though 3pm in UTC is technically before 4pm in New York the Compare functions will still indicate the 4pm is later.  This is to remain consistent with v1.0 and v1.1 behavior.

o        There are no new DateTimeKind aware comparison functions.

-          Deserializing a UTC time in v2.0 will not cause the time to shift on deserialization

o        In v1.0 and v1.1 the DateTime was always shifted a local time from the UTC offset.  In v2.0 the DateTime will remain unchanged but the DateTimeKind will be UTC

o        This means that round-tripping a DateTime through a service that only returns the UTC time will look like the time has shifted (see note on comparisons above) when it has really just been converted to UTC

 

How to get the old behavior back

 

In order to preserve backwards compatibility you can add the following section to your web.config and control the XmlSerialization of DateTime on a per application basis:

 

  <system.xml.serialization>

    <dateTimeSerialization mode="Local" />

  </system.xml.serialization>

 

Setting the dateTimeSerialization mode to Local will give you the v1.0 and v1.1 behavior.  Doing nothing or setting the value to Roundtrip will give you the new behavior that changes the serialization of DateTime based on the specified DateTimeKind.

 

Enjoy!

 

 

This is a long post so I’ll summarize my findings up front.

 

Summary:

 

  1. Following the .NET Remoting guidance enables quick and painless upgrade to WCF
  2. A single interface can be decorated to support .NET Remoting, WCF and ASMX
  3. The service implementation does not need to change at all to support all three platforms
  4. Wrapping the client connection code in a proxy allows developers to switch between .NET Remoting, WCF and ASMX on-the-fly

 

Read on for the details…

 

 

Details:

 

So you’ve read all the guidance about how and when to use .NET Remoting and now want to see a working code sample of this in action?

 

Building the .NET Remoting solution

 

I have broken this solution into three projects: 

 

  1. SharedContract – this contains the interface shared by client and service
  2. Service – this contains the service implementation of the interface in the SharedContract project and self-hosting code
  3. Client – this contains the client programming against the interface in the SharedContract project and the client proxy

 

NOTE: I repeat code blocks throughout this post and will highlight changes made in red.

 

Let’s start with an interface:

 

namespace SharedContract

{

    public interface IAmTheSharedContract

    {

        string Echo(string input);

    }

}

 

Using a shared interface is inline with the .NET Remoting guidance.  An interface based approach reduces the code shared between client and service and, as you will see, allows for re-use for other distributed applications stacks such as Windows Communication Foundation (WCF) and ASMX.  This is interface above is what every client will program against. 

 

Next we need to implement it:

 

namespace Service

{

    class ServiceImplementation : MarshalByRefObject, IAmTheSharedContract

    {

        public string Echo(string input)

        {

            Console.WriteLine("Received input: " + input);

            return input;

        }

    }

}

 

Next we need to host the service implementation.  Every distributed application stack has different hosting options and techniques.  Isolating this code from the implementation is critical to ensure easy migration and portability. 

 

Here is the code needed to host it for .NET Remoting:

 

namespace Service

{

    class Program

    {

        static void Main(string[] args)

        {

            RemotingConfiguration.Configure("Service.exe.config", true);

            Console.WriteLine("\nThe service is ready.");

            Console.WriteLine("Press <ENTER> to terminate service.\n");

 

            Console.ReadLine();

 

        }

    }

}

 

The configuration for hosting this .NET Remoting service is as follows:

 

  <!-- Remoting hosting section -->

  <system.runtime.remoting>

    <application>

      <service>

        <wellknown mode="SingleCall" type="Service.ServiceImplementation, Service" objectUri="server.rem" />

      </service>

      <channels>

        <channel ref="tcp" secure="true" port="8080" />

      </channels>

    </application>

  </system.runtime.remoting>

 

 

You will notice that I am using the new secure TcpChannel provided in .NET Remoting 2.0.  This new secure channel provides identification, encryption and signing simply by adding the secure=”true” attribute.

 

You now have a complete .NET Remoting service.

 

And now we need a client:

 

namespace Client

{

    class Program

    {

        static void Main(string[] args)

        {

            IAmTheSharedContract proxy = ClientProxyFactory.CreateProxyInstance();

            Console.WriteLine("Calling the " + ClientProxyFactory.Platform + " endpoint:");

            Console.WriteLine("\tResult: " + proxy.Echo("Testing " + ClientProxyFactory.Platform + " endpoint"));

        }

    }

}

 

You will notice that the client does create the proxy directly.  As per our guidance we are using a client proxy class to astract the .NET Remotingisms from the client code.  The proxy class looks like this:

 

namespace Client

{

    class ClientProxyFactory

    {

        static ClientProxyFactory()

        {

            RemotingConfiguration.Configure("Client.exe.config", true);

            defaultPlatform = (ClientProxyFactory.CommunicationPlatform)Enum.Parse(

                                typeof(ClientProxyFactory.CommunicationPlatform),

                                ConfigurationManager.AppSettings["communicationPlatform"]);

        }

 

        static CommunicationPlatform defaultPlatform;

        static ChannelFactory<IAmTheSharedContract> factory = null;

 

        public enum CommunicationPlatform

        {

            Remoting

        }

 

        public static CommunicationPlatform Platform

        {

            get

            {

                return defaultPlatform;

            }

        

        }

 

        public static IAmTheSharedContract CreateProxyInstance()

        {

            return CreateProxyInstance(defaultPlatform);

        }

 

        public static IAmTheSharedContract CreateProxyInstance(CommunicationPlatform platform)

        {

            IAmTheSharedContract proxy = null;

            switch (platform)

            {

                case CommunicationPlatform.Remoting:

                    proxy = (IAmTheSharedContract)Activator.GetObject(typeof(IAmTheSharedContract), "tcp://localhost:8080/server.rem") as IAmTheSharedContract;

                    break;

            }

 

            return proxy;

        }

    }

}

 

The ClientProxyFactory is used to create a proxy dependant on the distributed application platform.  The critical parts of this class are the constructor, where the chosen platform is set, and the CreateProxyInstace(), where the service proxy is create.  The distributed application platform to use is picked up from config and maps to the CommunicationPlatform enumeration.  The various connection URIs could be in config as well. 

 

The configuration for this client looks like this:

 

  <!-- client proxy config section -->

  <appSettings>

    <!-- use appSetting to configure base address provided by host -->

    <add key="communicationPlatform" value="Remoting" />

  </appSettings>

 

  <!-- Remoting configuration -->

  <system.runtime.remoting>

    <application>

      <channels>

        <channel ref="tcp" secure="true" tokenImpersonationLevel="identification" />

      </channels>

    </application>

  </system.runtime.remoting>

 

 

You will notice that I configure a channel for the client-side as well.  This is required for the secure TcpChannel to ensure that the client channel is enabled for security as well. 

 

And now you have a complete .NET Remoting application using the our .NET Remoting programming guidance. 

 

The next step is to enable the solution for WCF.

 

Enabling the solution for WCF

 

Now we’ll convert this application to use Windows Communication Foundation (WCF) (Indigo).

 

First we need to modify the contract:

 

namespace SharedContract

{

    [ServiceContract]

    public interface IAmTheSharedContract

    {

        [OperationContract]

        string Echo(string input);

    }

}

 

All we need to do is decorate the interface with the ServiceContract attribute and the methods to expose with the OperationContract attribute.  This will be automatically picked up by WCF to enable exposing this interface as a WCF endpoint.

 

What do we need to do to the implementation class?  NOTHING.

 

To support a WCF endpoint we need to add some hosting code:

 

namespace Service

{

    class Program

    {

        static void Main(string[] args)

        {

            RemotingConfiguration.Configure("Service.exe.config", true);

 

            // Get base address from app settings in configuration

            Uri baseAddress = new Uri(ConfigurationManager.AppSettings["baseAddress"]);

 

            ServiceHost serviceHost = new ServiceHost(typeof(Service.ServiceImplementation), baseAddress);

 

            // Open the ServiceHostBase to create listeners and start listening for messages.

            serviceHost.Open();

 

            Console.WriteLine("\nThe services are ready.");

            Console.WriteLine("Press <ENTER> to terminate service.\n");

 

            Console.ReadLine();

 

        }

    }

}

 

This hosting code gets the service address from config and then uses ServiceHost to configure the WCF endpoints.  The app.config file contains the configuration for WCF hosting. 

 

We need to add some code to configuration to enable the WCF endpoint:

 

  <!-- WCF hosting section -->

  <appSettings>

    <!-- use appSetting to configure base address provided by host -->

    <add key="baseAddress" value="net.tcp://localhost:8088/wcfservice" />

  </appSettings>

 

  <system.serviceModel>

    <services>

      <service

          type="Service.ServiceImplementation, Service">

        <!-- use base address provided by host -->

        <endpoint address=""

                  binding="netTcpBinding"

                  bindingConfiguration="Binding1"

                  contract="SharedContract.IAmTheSharedContract,SharedContract" />

      </service>

    </services>

 

    <bindings>

      <netTcpBinding>

        <binding name="Binding1" />

      </netTcpBinding>

    </bindings>

 

  </system.serviceModel>

 

 

This configuration establishes a TCP endpoint using the WCF NetTcpBinding. 

 

Now the service is hosting both .NET Remoting and WCF! 

 

On to the client…

 

The code in the client Main() from before does NOT change! 

 

The programming against the shared interface remains the same but we need to update the ClientProxyFactory to enable it to connect to WCF endpoints:

 

namespace Client

{

    class ClientProxyFactory

    {

        static ClientProxyFactory()

        {

            RemotingConfiguration.Configure("Client.exe.config", true);

            defaultPlatform = (ClientProxyFactory.CommunicationPlatform)Enum.Parse(

                                typeof(ClientProxyFactory.CommunicationPlatform),

                                ConfigurationManager.AppSettings["communicationPlatform"]);

        }

 

        static CommunicationPlatform defaultPlatform;

        static ChannelFactory<IAmTheSharedContract> factory = null;

 

        public enum CommunicationPlatform

        {

            Remoting,

            WCF

        }

 

        public static CommunicationPlatform Platform

        {

            get

            {

                return defaultPlatform;

            }

       

        }

 

        public static IAmTheSharedContract CreateProxyInstance()

        {

            return CreateProxyInstance(defaultPlatform);

        }

 

        public static IAmTheSharedContract CreateProxyInstance(CommunicationPlatform platform)

        {

            IAmTheSharedContract proxy = null;

            switch (platform)

            {

                case CommunicationPlatform.Remoting:

                    proxy = (IAmTheSharedContract)Activator.GetObject(typeof(IAmTheSharedContract), "tcp://localhost:8080/server.rem") as IAmTheSharedContract;

                    break;

                case CommunicationPlatform.WCF:

                    if(factory == null)

                        factory = new ChannelFactory<IAmTheSharedContract>("");

                    proxy = factory.CreateChannel();

                    break;

            }

 

            return proxy;

        }

    }

}

 

 

As you can see we simply added WCF to the CommunicationPlatform enumeration and then added code to create a proxy to the WCF endpoint using the ChannelFactory<T>.CreateChannel() API.

 

This requires additional client configuration for the WCF endpoint:

 

    <add key="communicationPlatform" value="WCF" />

 

  <!-- WCF hosting configuration -->

  <system.serviceModel>

 

    <client>

      <endpoint name=""

            address="net.tcp://localhost:8088/wcfservice"

            binding="netTcpBinding"

            bindingConfiguration="Binding1"

                contract="SharedContract.IAmTheSharedContract, SharedContract">

      </endpoint>

    </client>

 

    <bindings>

      <netTcpBinding>

        <binding name="Binding1" />

      </netTcpBinding>

    </bindings>

 

  </system.serviceModel>

 

We changed the CommunicationPlatform value to now use WCF and then added the configuration to point to the WCF endpoint.

 

Now you have a client that talks both to WCF and .NET Remoting! 

 

To change which endpoint you are talking to requires only that you change the CommunicationPlatform valule in the Client.exe.config file.

 

 

Enabling the solution for ASMX

 

Now you may be wondering if this conversion is possible to ASMX Web Services.  Yes, it is!

 

Here are the steps to convert to support ASMX as well.

 

First update the shared interface:

 

namespace SharedContract

{

    [ServiceContract]

    [WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1,EmitConformanceClaims=true)]

    public interface IAmTheSharedContract

    {

        [OperationContract]

        [WebMethod]

        string Echo(string input);

    }

}

 

As you can see, ASMX is enabled simply by further interface and method decoration to enable ASMX as well.  The WebServiceBinding attribute is new in .NET Framework 2.0 and allows ASMX to support declaration of your web service methods through an interface.

 

What changes in the service implementation?  NOTHING!  That is pretty cool.  

 

ASMX in .NET Framework 2.0 will automatically pick up the fact that the service implementation implements and interface marked with the WebServiceBinding attribute and will generate a WSDL based on the interface methods marked with WebMethod.

 

If you want to change the namespace of the service then you can decorate the implementation class with a WebService attribute and control the namespace as well.  This will NOT interfere with hosting this class in .NET Remoting or WCF.  

 

Next we look at the hosting problem.  When I built this sample I took the simple route:

 

  1. I changed the Service project build properties to output to bin\ instead of bin\Debug to support the IIS model of <app>\bin
  2. Then I created a VDir that pointed to the folder where the service code lives.
  3. Then I added an WebService.asmx file to the service project.  The code is below:

 

 

<%@ WebService language="C#" class="Service.ServiceImplementation,Service" %>

 

Because of the changes in .NET Framework 2.0 the ASMX file will be compiled on the fly so no building is required.  If you go to http://localhost/<VDir>/WebService.asmx you will see that the ASMX web service is now hosted.  Go ?wsdl to make sure that the WSDL is also generated. 

 

You are now hosting this same interface and implementation in ASMX as well.

 

Next we move on the client.  On the client-side we need to generate and ASMX client proxy.  Use wsdl.exe /out:asmxProxy.cs http://localhost/<VDir>/WebService.asmx?wsdl to generate the client proxy class. 

 

The generated proxy needs to be changed in the following ways:

 

  1. Change the name (and the ctor) of the proxy class to a different name (e.g., AsmxClientProxy) to avoid conflicts with the shared interface (SharedContract.IAmTheSharedContract)
  2. Change the client proxy to implement SharedContract.IAmTheSharedContract by changing the class declaration to indicate that the proxy class implements the interface

 

The code in the client Main() from before does NOT change! 

 

And the ClientProxyFactory needs to be changed as well:

 

namespace Client

{

    class ClientProxyFactory

    {

        static ClientProxyFactory()

        {

            RemotingConfiguration.Configure("Client.exe.config", true);

            defaultPlatform = (ClientProxyFactory.CommunicationPlatform)Enum.Parse(

                                typeof(ClientProxyFactory.CommunicationPlatform),

                                ConfigurationManager.AppSettings["communicationPlatform"]);

        }

 

        static CommunicationPlatform defaultPlatform;

        static ChannelFactory<IAmTheSharedContract> factory = null;

 

        public enum CommunicationPlatform

        {

            Remoting,

            WCF,

            ASMX

        }

 

        public static CommunicationPlatform Platform

        {

            get

            {

                return defaultPlatform;

            }

       

        }

 

        public static IAmTheSharedContract CreateProxyInstance()

        {

            return CreateProxyInstance(defaultPlatform);

        }

 

        public static IAmTheSharedContract CreateProxyInstance(CommunicationPlatform platform)

        {

            IAmTheSharedContract proxy = null;

            switch (platform)

            {

                case CommunicationPlatform.Remoting:

                    proxy = (IAmTheSharedContract)Activator.GetObject(typeof(IAmTheSharedContract), "tcp://localhost:8080/server.rem") as IAmTheSharedContract;

                    break;

                case CommunicationPlatform.WCF:

                    if(factory == null)

                        factory = new ChannelFactory<IAmTheSharedContract>("");

                    proxy = factory.CreateChannel();

                    break;

                case CommunicationPlatform.ASMX:

                    proxy = new AsmxClientProxy() as IAmTheSharedContract;

                    break;

            }

 

            return proxy;

        }

    }

}

 

As you can see we simply added ASMX to the CommunicationPlatform enumeration and then added code to create an instance of the generated ASMX proxy and return this proxy as the shared interface.

 

Now you can change the Client.exe.config to use the ASMX endpoint as well:

 

    <add key="communicationPlatform" value="ASMX" />

 

 

And now you have one client that can talk to .NET Remoting, WCF and ASMX!  All of these use the same shared interface! 

 

Key Takeaways:

 

  1. Follow the .NET Remoting guidance
  2. Use interfaces to define service contracts since they translate well to ASMX, WCF and .NET Remoting
  3. Service implementations should not take any dependency on the hosting model or distributed application platform to enable migration and portability
  4. Abstracting proxy creation and hosting code can help with easier migration in the future

 

 

 

 

Here is the final trick.  And those of you who have read the series have waited a while for this final post.  (Sorry for the delay.)  Programming against an interface gives you the consistent programming model across the various distributed platforms but abstracting the code that creates the proxy is also critical to hide the .NET Remotingisms from your client code as well. 

 

Take for example the following line from the previous post:

IHello proxy = (IHello)Activator.GetObject(typeof(IHello), "tcp://localhost/Hello.rem");

The use of the Activator class implies a specific distributed application stack so hiding that from your implementation will ease migration to alternate or future stacks with limited client impact.  The same holds true for the hosting code written to register objects and channels but this code is often already isolated in the service startup routines (e.g. Main or OnStart for a service).

 

The easiest way to abstract away the proxy generation code is to use a basic factory pattern on the client that creates the proxy classes.  This has the added advantage of enabling the developer to use pooling or handle lifetime issues but the core value for this conversation is the abstraction of proxy creation.

 

Here is a simple example:

 

public static class HelloClientProxyFactory

{

            public static IHello CreateHelloProxy()

            {

                        return (IHello)Activator.GetObject(typeof(IHello), "tcp://localhost/Hello.rem");

            }

}

 

As you migrate forward from .NET Remoting to other platforms (e.g., Indigo) this proxy factory can be updated and deployed to your clients enabling the client programming model to remain entirely unchanged but allow your developers to move to a completely different distributed application stack underneath.

 

This concludes the Shhh… series but I have more post planned for the migration story from .NET Remoting to WCF (“Indigo”).

 

Stay tuned.

 

A little self-promotion never hurt anyone...

Some new MSDN TV content has recently been made available on the new features of .NET Remoting for .NET Framework 2.0.  Enjoy!

http://msdn.microsoft.com/msdntv/episode.aspx?xml=episodes/en/20050120NETMT/manifest.xml

Matt Tavis shows some new features and code examples in .NET Remoting in .NET Framework 2.0, including the new IpcChannel, the secure TcpChannel, and Version Tolerant Serialization (VTS) to allow authors to version their types without breaking serialization.

Now that you expose the your remote object through a CLR interface, that interface should limit itself to interfaces and serializable types as parameters and return values.  This guideline is really just an additional follow-on to rule one about using only CLR interfaces.

Let's expand on the original example from part 1.  The original interface from part 1 is below:

public interface IHello
{
   string HelloWorld(string name);
}

This works fine since the the HelloWorld accepts and returns only strings which are serializable. What if we want to add a factory pattern that returns newly created remote objects or make the HelloWorld method take a custom type.  For these scenarios we should continue to use interfaces and define serializable data types for passing of information.

First let's add the factory pattern:

public interface IHelloFactory
{
   IHello CreateHelloService();
}

This factory pattern is also an interface and simply returns the original IHello interface.  The implementation of this interface would inherit from MBRO and new up a HelloObject but all the clients of this service would continue to use the shared interfaces for programming rather than the implementation classes.

Now we expand HelloWorld to take the new HelloMessage custom data types that is serializable.

public interface IHello
{
   HelloMessage HelloWorld(HelloMessage message);
}

[Serializable]
public class HelloMessage
{
   private string SenderName;
   private string Text;
   // property accessors omitted for brevity
   ...

   public HelloMessage(string sender, string text)
   { ... }
}

By sticking to this guidance we continue to deploy only the types necessary to share the contract.  Shared interfaces (IHello and IHelloFactory) and serializable data types (HelloMessage) are the only pieces of the contract that consumer of our remote service need ever program against.  Even if you move to a different activation, hosting or communications technology these same contract types will be reusable and are not .NET Remoting specific.  Upgrading from this model to a future technology will be simple and mechanical since the programming interface will remain constant once an instance of the service is activated and accessible.

The new client code will allow for activation of the factory and then creation and consumption of the IHello service:

IHelloFactory factoryProxy = (IHelloFactory)Activator.GetObject(typeof(IHelloFactory), "tcp://localhost/HelloFactory.rem");
IHello helloProxy = factoryProxy.CreateHelloService();
HelloMessage returnMessage = helloProxy.HelloWord(new HelloMessage("Me", "Hello"));

 

In Part 1 it was suggested to use only interfaces to program against your remoted object.  Another consideration for enforcing the correct usage of your remote object is to limit access to the implementation except through the interface that you defined for it.

You can do this by marking the implementation internal so that it is cannot be constructed directly except within the same assembly. 

Consider the following changed code from Part 1:

Server code:
internal class HelloObject : MarshalByRefObject, IHello
{
   string IHello.HelloWorld(string name)
   {
      return "Hello, " + name;
   }
}

Shared code:
public interface IHello
{
   string HelloWorld(string name);
}

The same code on the client will allow for activation of an instance of the HelloObject class:

IHello proxy = (IHello)Activator.GetObject(typeof(IHello), "tcp://localhost/Hello.rem");

But this will prevent someone from using the type outside the implementation assembly by constructing the class directly: 
// this will now fail outside the implementation assembly
HelloObject localObject = new HelloObject();

 This may seem a bit draconian and unnecessary but it can enforce two good early development practices:

1.  If you plan on "remoting" an object in the future don't treat it as "local" during development.  This can lead to performance and scalability issues that start with "But it work fine running locally on my dev box...".

2.  Enforce your contract (i.e., the IHello interface) as the only way to program against your remote object.  If you are planning on "remoting" an object then enforcing a common programming model, whether local or remote, can help ensure consistency and avoid issues when deploying your remote object later.

Even after following this advice you can access the implementation class locally without remoting it by using the Activator class:

IHello localObject = (IHello)Activator.CreateInstance(Type.GetType("HelloObject"));

This allows local developers to use the implementation class but keeps the programming interface common whether local or remote.

 

One of the easiest ways to avoid locking yourself into .NET Remoting is to avoid exposing its most infamous type in your contract:  MarshalByRefObject (MBRO).  To marshal object references in .NET Remoting your type needs to inherit from MBRO but that doesn't mean your contract needs to expose types that inherit from MBRO.

Use CLR Interfaces for the remote objects in your contract. 

This isn't really new advice.  Ingo Rammer has suggested this in his book as well.  Interestingly, those of you following the Indigo programming model will have seen a lot of interface usage so this pattern will carry forward nicely. 

Let's take an example:

Server code:
public class HelloObject : MarshalByRefObject, IHello
{
   string IHello.HelloWorld(string name)
   {
      return "Hello, " + name;
   }
}

Shared code:
public interface IHello
{
   string HelloWorld(string name);
}

Now you can register HelloObject on the server side but get an instance of the remote object with the following call:

IHello proxy = (IHello)Activator.GetObject(typeof(IHello), "tcp://localhost/Hello.rem");

There are some major advantages to this approach:

- Only the interface needs to be shared, which allows for greater flexibility in versioning and deployment
- The interface (and hence contract) that has been established can be re-used later as part of the migration to Indigo

There is one disadvantage to this approach:

- Interception of new() is not supported

I will talk about how to get this support back in a future section of this series.

Q&A and updates---

Here are some good questions I got on this post with some of my answers...

Q: Why by exposing a type that inherits from MarshalByRefObject I'll get locked into Remoting? What you said in your post is a good code practice but not necessary.

A:  .NET Remoting requires the sharing of types between client and server.  Imagine that a new programming model came along that used a new infrastructure class for intercepting object creation and proxy generation (MBRO2). Keeping your interface seperate from your implementation allows you switch from MBRO to MBRO2 without changing the contract with the server or the type that you program against (i.e., the interface).  You might have to change activation code but the programming experience against the remoted object can remain constant.  Like you say, it isn't required but it is a good practice. 

Q: MBRO belongs to System namespace and not Remoting, any time you cross app domain boundaries to call your object you need it's ObjRef so it is not remoting specific to inherit from MBRO.

A:  Crossing AppDomain boundaries within a process using MBRO *is* .NET Remoting.  It isn't the same exact programming model for activation but it is using the .NET Remoting infrastructure. 

So you've heard about SOA and Indigo and the future of distributed application development on .NET.  You've even seen the long-running discussion of our guidance on Richard Turner's blog.  You are now asking yourself:

But how can I use .NET Remoting today but be prepared for Indigo? 

Hopefully, I can answer all of your questions in this multi-part series.  The idea is to give some practical guidance with some code snippets to show you how to use .NET Remoting today without painting yourself into tomorrow's corner.

 

Now that I have your attention...

We are seriously considering deprecating the SoapFormatter in .NET Framework 2.0.  It is the nexus of a whole host of serialization issues and implies a promise of interop that it does not and will not live up to.  It also does not support generics.  Additionally, those of you interested in the new Version Tolerant Serialization features in .NF 2.0 will note that it is not supported on the SoapFormatter.  This means that types using VTS to introduce new fields would break when serializing between versions over the SoapFormatter.  This is also true for framework types.  So if you are using a framework type that adds fields in .NF 2.0 and you do not upgrade both apps using the SoapFormatter to .NF 2.0 then you could see serailization errors occuring simply by upgrading to .NF 2.0 on one side.  Before you ask, no I don't have a list of framework types that might break in this scenario but the question is whether requiring switching to the BinaryFormatter is unacceptable.

The obvious solution to this is to switch to the BinaryFormatter. It supports VTS, the serialization of generics and is one of the Cross-AppDomain formatters, where our guidance recommends the usage of .NET Remoting for new applications.

This is our stance as of Beta1 for .NET Framework 2.0.

So the questions is:  Who has to have the SoapFormatter in .NF 2.0 and why?

I'll be going to TechEd Europe to give a few talks.  If you are interested in any of the following then add them to your schedule:

  • Wed 8:30  CTS201  Web Service, XML Serialization and Networking in .NET Framework 2.0
    • A preview of some of the cool new features in the Technology Preview
  • Thur 8:30  CTS300  Prescriptive Guidance-Juggling Web Services, WSE, .NET Remoting, System.EnterpriseServices, and MSMQ
    • What to use when. And what is Microsoft's guidance.
  • Thur 10:15 CTS406  Versioning of Connected .NET Applications
    • Versioning guidance at the contract and type level.

For anyone interested in talking about .NET Remoting specifically contact me through the TechEd site and I would be happy to meet with you. 

I'll be in Amsterdam from Monday to Thursday.

Interested in Web Services features in Whidbey?  So am I!

Yasser Shohoud, Software Legend, and I will be giving a talk at TechEd to introduce some our favorite new Web Services features to you at TechEd.  The new talk (CTS201) is not even in the Session Catalog yet but never fear Yasser and I will be giving it.  It is planned for the Friday at 2:45pm slot so mark your calendars!

We'll be covering Web Services, XmlSerialization and System.Net features to make your Whidbey Web Services faster, easier to write, more interoperable and more flexible.  See you there!

Those of you scouring the list of talks at TechEd for in-depth .NET Remoting discussions will be disappointed.  .NET Remoting will be touched on in the Connected Systems track (CTS300) by Richard Turner but that will be prescriptive guidance for the usage of .NET Remoting.  There isn't a session dedicated to .NET Remoting at this TechEd.

But the good news is that I'll be at TechEd and would be happy to sit down with interested folks and talk through current and future .NET Remoting features!  I'll be at TechEd all week and would be happy to talk to you about .NET Remoting. 

Just post a response to this entry and I'll see if I can setup and impromptu meeting at TechEd for those interested in .NET Remoting to help you out.

As a first blog entry, I figure introductions are in order.  I am Matt Tavis, PM at Microsoft working on the .NET Framework 2.0 (Whidbey) and Indigo.  My Whidbey features areas are .NET Remoting, runtime serialization and xml serialization. 

 

 
Page view tracker