Introduction
The RemotingXml project was developed with two purposes in mind:
- show how to use marshal by value technology;
- show how to implement a remote storage.
The RemotingXml project consists of a client and a server. The client creates and works with objects, and sends them to a server using the marshal-by-value technology to be stored in XML files. The client is also able to request the objects from the XML files.
This project might be helpful to those who want to implement some kind of remote persistence solution. In this example, XML files are used as a very simple and comprehensive example; however, the project can be easily used as a template to build a database or file-based remoting storage solution.
Background
Though marshal-by-reference remoting is widely covered on the net, marshal-by-value still tends to create some confusion. This project is created to clear up this confusion and show an example of how marshal-by-value objects can be used.
Solution Organization
RemotingXml consists of three projects:
- Server: contains the code for a remote server. The server project is responsible for creating and starting remoting services.
- Client: contains the code for creating persistent classes, sending them for serialization to the server, and requesting the server for objects from the XML files.
- PersistentClasses project: is a library containing the definitions for classes used on the client and on the server. These include: class to be serialized, list wrapper for the serialized class, and the marshal-by-reference class used for persistent classes transport.
Persistent Classes
Persistent classes are the classes that will be created on the client and serialized on the server. Any class can be persisted if it either has the Serializable
attribute or implements the ISerializable
interface. In this example, the House
and Address
classes are used.
The Address
class is saved as a field in the House
class:
[Serializable]
public class House
{
private Address _address;
private string _owner;
�.
}
In order to save a list of House
s, we will need to create a special List
implementation:
[Serializable]
[XmlInclude(typeof(House)), XmlInclude(typeof(Address))]
public class HouseList<T> : List<T>
{
}
This implementation is required for two purposes: an object passed to a remote server should be serializable; the object needs to provide the details of the included classes for correct serialization.
Transport Class
In order to send the persistent classes from the client to the server, we will need a Marshal-By-Reference object:
public class XmlHandler: MarshalByRefObject
{
private const string Extension = ".xml";
private IList _persistentObjects;
public IList PersistentObjects
{
get { return _persistentObjects; }
set { _persistentObjects = value; }
}
public void StoreData()
{
System.Console.WriteLine("persistentObjects " +
_persistentObjects.Count);
if (_persistentObjects.Count > 0)
{
object persistentObject = _persistentObjects[0];
XmlSerializer serializer =
new XmlSerializer(_persistentObjects.GetType());
string filename = persistentObject.GetType().Name + Extension;
File.Delete(filename);
StreamWriter xmlWriter = new StreamWriter(filename);
serializer.Serialize(xmlWriter, _persistentObjects);
xmlWriter.Close();
}
}
public IList RetrieveData(Type arrayType, Type objectType)
{
string filename = objectType.Name + Extension;
XmlSerializer serializer = new XmlSerializer(arrayType);
FileStream xmlFileStream = new FileStream(filename, FileMode.Open);
IList objects = (IList)serializer.Deserialize(xmlFileStream);
xmlFileStream.Close();
return objects;
}
}
XmlHandler
holds a reference to the persistent objects array in the _persistentObjects
variable. This variable is passed from the client.
As you can see, there are methods for serializing and deselializing objects. These methods are executed on the server.
Server
The server project is presented by a single XmlServer
class, the role of which is to start the XmlHandler
service.
class XmlServer
{
static void Main(string[] args)
{
HttpChannel channel = new HttpChannel(65101);
ChannelServices.RegisterChannel(channel);
Type XmlHandler =
Type.GetType("RemotingXml.PersistentClasses.XmlHandler,
PersistentClasses");
RemotingConfiguration.RegisterWellKnownServiceType(
XmlHandler,
"XmlHandlerEndPoint",
WellKnownObjectMode.Singleton
);
Console.WriteLine("XmlHandler is ready.");
Console.WriteLine("Services are running. Press Enter to end...");
Console.ReadLine();
}
}
Client
The client project contains the Program
class. In the constructor, a connection to the XmlHandler
service is established:
public Program()
{
string url;
HttpChannel channel = new HttpChannel(0);
url = @"http://LocalHost:65101/";
ChannelServices.RegisterChannel(channel, false);
xmlHandler = (XmlHandler)RemotingServices.Connect(
typeof(PersistentClasses.XmlHandler), url +
"XmlHandlerEndPoint"
);
}
The RunTest
method creates a list of House
objects, sends them to the server using the XmlHandler
, and retrieves the objects from the XML files on the server:
private void RunTest()
{
House house = new House(new Address("East London",
"266 Oxford Street"), "Derick Hopkins");
HouseList persistentObjects = new HouseList();
persistentObjects.Add(house);
house = new House(new Address("East London",
"23 Stewart Drive"), "Om Henderson");
persistentObjects.Add(house);
xmlHandler.PersistentObjects = (IList)persistentObjects;
xmlHandler.StoreData();
IList result = xmlHandler.RetrieveData(typeof(HouseList),
typeof(House));
foreach (object obj in result)
{
System.Console.WriteLine(obj);
}
}
Points of Interest
While creating this project, I had an idea in mind to make it completely generic by using generics for persistent object collections. However, the idea was not realized due to the fact that the SOAP serializer does not support generics and explicit using of binary serializers on the client and the server still does not solve the problem.
History
- 2007-05-25 - First version.