Be explicit!

This article was originally published in VSJ, which is now part of Developer Fusion.
The 1990s and the beginning of the 21st century were not only – as discussed in the previous issue – the age of object-oriented in-memory representation of data. In the same vein, object-oriented protocols for the development of distributed applications were state of the art. From CORBA, Java RMI, DCOM, to .NET Remoting – no platform has been complete without object-oriented remote procedure calls (OORPC). One nevertheless has to pose a similar question as with object-oriented database access: are these technologies still the best choice if you want to focus on the maintainability, scalability and performance of an application?

OORPC is very intriguing for most developers as it can work transparently behind the scenes. You can, for example, simply pass object references across process and platform boundaries. Even callbacks and events can be encapsulated by the frameworks. Total transparency. You do not have to worry about any details – what a great technology.

Complete transparency…

But is it really? Just look at the following piece of source code and try to answer two of my questions afterwards:
public class Calculator:
		MarshalByRefObject
{
	public int Add(int x, int y)
	{
		return x + y;
	}
}

public class MyApplication
{
	public static void
		PerformCalculation()
	{
		Calculator calc =
			new Calculator();
		int result = 10;
		for (int i = 0; i<100; i++)
		{
			result = calc.Add(result,
				10);
		}
	}
}
The first – and for business applications most important – question I’d like to ask is: “In which approximate performance range (microseconds, milliseconds, seconds, minutes) will the execution PerformCalculation() take place?” The second, and no less interesting question is, “How likely is the occurrence of an Exception in this method?” If you were just about to answer “microseconds” and “unlikely or impossible”, then unfortunately I have to disappoint you.

Just imagine that – somewhere buried in the thousands of lines of code of your application – the following statement of exists:

RemotingConfiguration.
	RegisterWellKnownClientType(
	typeof(Calculator),
	“http://srv01/calc.rem”);
The class Calculator would in this case be registered with .NET Remoting, so that the “new” operator will not create a new local Calculator object, but instead a reference to a remote server-side object. In this case, the execution of PerformCalculation() might take several seconds and might throw exceptions in case of network problems. The problem with this piece of code is that the Remoting boundary has not been chosen explicitly, so that you would expect a different behaviour – and a totally different potential for errors – when looking at the code.

…or better none at all?

On the other end of the communication technology spectrum, you’ll find products like Microsoft Message Queue Server (MSMQ).

This technology won’t give you any additional layers of abstraction or transparency. Instead it provides just the possbility to send messages to certain receivers. In C# this could, for example, look like the following:

class Client
{
	public static void Main()
	{
		MessageQueue q =
			new MessageQueue(
@”MYSERVER01\private$\MyServerQueue”);
		Message msg = new Message();
		msg.Formatter = new 
System.Messaging.XmlMessageFormatter(
			new Type[]
			{typeof(MyMessage)});
		msg.Body = new
			MyMessage(“Hello, world!”);
		q.Send(msg);
	}
}

class MyMessage
{
	public String Text;
	public MyMessage(String txt)
	{
		Text = txt;
	}
}
In this case, the class MyMessage is automatically serialised when assigned to msg.Body, and is afterwards sent to the specified destination address (MYSERVER01\private$\MyServerQueue). A server-side application can now wait for messages arriving at the specified queue. As soon as a message is received, the server application can de-serialise the body to regenerate a MyMessage object and act upon it. This reaction might also include sending a corresponding response message to the client.

Here, however, I don’t want to talk about the technological aspects of distributed applications, but instead about psychology and the possibility of avoiding mistakes. To look into these possibilities, please again compare these two code snippets:

result = calc.Add(result, 10);
…and:
msg.Body = new MyMessage(
		“Hello, world!”);
q.Send(msg);
…and imagine that you have just inherited a huge application from another developer. If one of these two fragments were contained inside some 60,000 lines of code, which one would you prefer? The one where you can’t recognise that a remote procedure call is taking place, or the second one for which the nature of this interaction is immediately visible?

A middle course

I am afraid that most developers will deem the manual creation of messages as overly complicated for a large number of applications1. But even in this case, should you prefer an explicit creation of remote references? Instead of using RemotingConfiguration.Configure(), RemotingConfiguration.RegisterWellknownClientType() and the overloaded “new” operator to create proxies to remote components, you can choose a more explicit approach.

If, for example, you choose the .NET Remoting framework, you’d start with the creation of explicit interfaces to every server-side object as in the following:

public interface
		IRemoteCustomerManager
{ void AddCustomer(Customer cust); }
You can then implement this interface in a server-side MarshalByRefObject and register it with the .NET Remoting framework. On the client side, you can continue using the standard Remoting configuration files to register the newly created interface:
<configuration>
	<system.runtime.remoting>
		<application>
			<client>
				<wellknown type=
				“General.IMyRemoteObject,
				General”
	url=”http://server/someobject.rem”
				/>
			</client>
		</application>
	</system.runtime.remoting>
</configuration>
When using the RemotingHelper class which is shown here:
class RemotingHelper
{
	private static bool _isInit;
	private static IDictionary _wellKnownTypes;

	public static Object CreateProxy(Type type)
	{
		if (! _isInit) InitTypeCache();
		WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)
			_wellKnownTypes[type];

		if (entr == null)
		{
			throw new RemotingException(“Typ nicht registriert!”);
		}

		return Activator.GetObject(entr.ObjectType,entr.ObjectUrl);
	}

	public static void InitTypeCache()
	{
		Hashtable tmp =new Hashtable();
		foreach (WellKnownClientTypeEntry entr in
			RemotingConfiguration.GetRegisteredWellKnownClientTypes())
		{
			_wellKnownTypes.Add (entr.ObjectType,entr);
		}
		_wellKnownTypes = tmp;
		_isInit=true;
	}
}
…you now get the ability to use client-side code like:
class Client
{
	public static void Main()
	{
		RemotingConfiguration.Configure(
			“client.exe.config”);
		IRemoteCustomerManager mgr = 
			(IRemoteCustomerManager);
		RemotingHelper.CreateProxy(
			typeof(
			IRemoteCustomerManager));
		mgr.AddCustomer(...);
	}
}
Even when looking at this piece of code months after you’ve written it, it will be immediately obvious: you are accessing a remote component.

By the way: the future Indigo technology which will be introduced by Microsoft over the next few years will also require explicit proxy creation. With this technology, you can use the overloaded “new” operator but you will have to call:

RemoteCustomerManager mgr =
	ServiceManager.CreateProxy
	<RemoteCustomerManager>();
…to create a remote proxy.

But you don’t have to wait for Indigo to create more readable source code: you can start today to use explicit interfaces between client and server.


Ingo Rammer is founder of Thinktecture, a company supporting software architects and developers with the design and architecture of .NET and Web Services applications. He is a regular speaker about these topics at conferences around the world, the author of the books Advanced .NET Remoting and Advanced .NET Remoting in VB.NET, and Microsoft Regional Director for Austria. You can reach him at www.thinktecture.com/staff/ingo.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.” - Donald Knuth