ServicesResourcesConferences Our TeamWeblogsAboutContact
     
  

Developer Resources

  Architecture Briefings
  Articles
  Conversations
  Tools and Samples
  Books

SoapSuds vs. Interfaces in .NET Remoting

In a recent newsgroup post, I recommended to use Interfaces to access remote objects instead of using SoapSuds. My exact words were "I'd generally recommend to avoid SoapSuds whenever possible."

Generally speaking, the idea behind SoapSuds is to run it on an existing assembly to extract the metadata for all MarshalByRefObject so that you don't need to deploy the complete implementation assembly to your clients.

This goal is pretty ambitious, and in fact: It just can't work. As soon as you have [Serializable] or ISerializable classes in your assembly, you are pretty much on your own because SoapSuds will only extract the metadata (i.e. the fields) but not the implementation.

Just imagine that you have a class like this:

[Serializable]
public class Foo
{
   private String _bar;

   public String Bar 
   {
     get { return _bar; }

     set 
     { 
        if (value.Length > 30) 
        {
            throw new ApplicationException(
                 "Bar might not be longer than 30 chars");
        }
        _bar = value;
     }
  }
}

When running SoapSuds here, it will extract only the metadata which basically leaves you with the following class which is quite a bit different.

[Serializable]
public class Foo
{
   public String Bar;
}

But even if you can work around this issue (by including your [Serializable] classes in a different assembly which is shared between server and client), you might still run into some issues.

As soon as your application increases in complexity, you encounter one or more of the following problems:

  • Typed DataSets don't work
  • If you use System.ComponentModel.Component (and some others) SoapSuds will simply throw an exception instead of generating anything
  • Various conditions trigger the generation of non-compilable code (duplicate using-statements in a file, ...)
  • Async calls via Delegate.BeginInvoke() won't work
  • ... some more problems which I can't remember anymore, but for which I could dig up the repro-code if anyone is interested ...

One of the reasons for using SoapSuds is the abiltiy to register this metadata-only classes at the client side so that you can basically use the new-operator to instantiate remote references.

But at the end of the day, location transparency - as it is implied in this case - is dangerous. It can affect your application's performance in negative ways (for example when using way too chatty interfaces). Normally, you should know exactly which method will be executed remotely and which class will run in a remote context - therefore my conclusion:

  • Use explicitly defined remote interfaces
  • Use a helper class like the one shown here if you want to go with configuration files.
  • Use factory SAOs instead of CAOs ("activated"-types don't work with interfaces, therefore: factory). [Or even better: avoid CAOs if possible. They affect scalability.]

Let me give you one more reason why I definitely advocate the use of explicit interfaces to access remote objects:

Just imagine that you inherit client-side code written by someone else and you see code like the following.

private double CalculateSum(Order o)
{
    double sum = 0;
    foreach (OrderDetail od in o.Details)
    {
         sum = sum + od.LineTotal;
     }
     return sum;
}

Your user complains that the "application is too slow" but you just can't find any problems. You didn't instantly notice that Order and OrderDetail are actually configured as remote objects and a single execution of this method might result in a dozen or more network round trips.

If you would have used interfaces instead, problems like this can be much more obvious:

private double CalculateSum(IRemoteOrder o)
{
    double sum = 0;
    foreach (IRemoteOrderDetail od in o.Details)
    {
         sum = sum + od.LineTotal;
     }
     return sum;
}

Actually, if the initial developer would have used interfaces right from the start, the code might have looked differently. As soon as he noticed that this method would involve multiple round-trips, he might have changed the interface and calculated the complete value at the server:

private double CalculateSum(IRemoteOrder o)
{
    return o.CalculateSum();
}

That's why I use interfaces. They help me to avoid mistakes.






 

© 2002-2006 by Thinktecture, Ingo Rammer and Christian Weyer. All rights reserved. | Contact | Impressum