Introduction
In .NET Remoting, Singleton, SingleCall and Client Activation are the three different ways, which can be used to access a Server-side object on the client through MBR (MarshalByReference). This article uses a sample application which compares and simplifies these three concepts.
I have tried to limit the size of this article and focused on the main topic. Following are the introductions of these three concepts:
- Singleton: Same instance of a Server-side object is used by all the clients for all the requests.
- SingleCall: Whenever a new call is received, a new instance of the Server-side object is created and reference is passed to the client. Therefore, for each call to the server, the client gets a new instance of the object.
- Client Activation: In this, the Client requests to the server for creating a new instance which can be used for multiple calls. The instance activated by a client is not used by other clients. Client-Activated objects can store state information between method calls for its specific client.
Please note that you must derive that class from MarshalByRefObject
, which is to be exposed to the clients to get the reference.
Singleton
As mentioned earlier, one Singleton object on the server is used by all the clients.
On the Server:
Mention the following settings in the "App.config" file.
<configuration>
<system.runtime.remoting>
<application name="MyRemoting">
<service>
<wellknown mode="Singleton" type="MyRemoteService.MyService,
MyRemoteService" objectUri="MyService.rem"/>
</service>
<channels>
<channel ref="http" port="9999" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
In the above code, type
contains the type of the object to be exposed on the server. objectUri
is any specific name assigned to this object which will be used by the client while mentioning the server URL. Any specific port
is specified for the channel to be used.
Include System.Runtime.Remoting
and write the following block of code to configure these settings on the server.
RemotingConfiguration.Configure("MyRemoteServer.exe.config");
Please note that you will need to replace your config file name in place of "MyRemoteServer.exe.config" in the above statement, which will be your "AssemblyName.config".
On the Client:
Include System.Runtime.Remoting
in your class and write the following code to create the proxy for the Singleton Server-side object.
MyService mySingletonRem = (MyService) Activator.GetObject(typeof(MyService),
"http://localhost:9999/MyRemoting/MyService.rem");
URL mentioned above is like ProtocolUsed://ServerAddress:PortNumber/ApplicationName/ObjectUri
SingleCall
Whenever a new call is received, a new instance of the Server-side object is created and reference is passed to the client. Therefore, for each call to the server, the client gets a new instance of the object.
To create SingleCall objects, everything will be same as in case of Singleton objects as mentioned above, but mode
specified in the App.Config file on the server, which will be "SingleCall" in place of "Singleton".
Client Activation
In this, the Client requests to the server for creating a new instance which can be used for multiple calls. The instance activated by a client is not used by other clients. Client-Activated objects can store state information between method calls for its specific client.
On the Server:
Mention the following settings in the "App.config" file.
<configuration>
<system.runtime.remoting>
<application name="MyRemoting">
<service>
<activated type="MyRemoteService.MyService, MyRemoteService" />
</service>
<channels>
<channel ref="http" port="9999" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
On the Client:
Include System.Runtime.Remoting
and System.Runtime.Remoting.Activation
and write the following block of code.
object[] activateAttribute =
{new UrlAttribute("http://localhost:9999/MyRemoting")};
MyService myClientActivatedRem =
(MyService) Activator.CreateInstance(typeof(MyService),
null, activateAttribute);
About Sample Application
The application creates all of the three types of objects of the same class. Although, in real time projects, we will rarely get a case to create all kinds of objects of the same class. I just created them for the same class to understand and compare them easily.
Each sample object maintains a counter of the number of Client calls received by it. It returns a message which includes that counter on calling a method ServeAll()
.
Each client creates proxies for all of the three kinds of Server-side objects on startup. There are three buttons in the client; i.e., "Singleton", "Client Activated" and "SingleCall". Clicking on the buttons, sends a call to respective kind of Server-side object. That means, on clicking "Singleton" button, client calls the method ServeAll
of the "Singleton" object.
Running Sample Application
- To run the demo, unzip all the demo files to a folder. Now, you can see there are two folders, one is MyRemoteServer and the another one is MyRemoteClient.
- Now, start the server by executing the file "MyRemoteServer.exe" in the MyRemoteServer folder.
- Start a client by executing the file "MyRemoteClient.exe" in the MyRemoteClient folder.
- Start another client by executing the file "MyRemoteClient.exe".
- Now, click on the buttons "SingleCall", "Singleton" and "Client Activated", one-by-one on both the clients.
- You will notice the following results.
- On clicking "SingleCall", the Server-side object will always say that "I have received 1 Client Requests so far.". Though, the number of SingleCall requests sent from the client increases on every click (This is reflected on the lower part of the window). This is due to the fact that each method call from the client is served by a new Server-side object in case of SingleCall objects.
- On clicking "Client Activated", the Server-side object says that "I have received X Client Requests so far.", where X will be same as the number of ClientActivated requests sent from a particular client. That means, if first client has sent three requests to Client Activated object, that object will say "I have received 3 Client Requests so far.", no matter how many requests are sent by second client, as clients do not share their ClientActivated objects with other clients.
- On clicking "Singleton", the Server-side object says that "I have received X Client Requests so far.", where X will be the sum of number of Singleton requests sent from all the clients. That means, if first client has sent three requests to Singleton object and second client has sent 6 requests, that Server-side object will say "I have received 9 (i.e. 3 + 6) Client Requests so far.". This is due to the fact that both of the clients share the same Server-side Singleton object.
Inside of the Sample Application
Following is Service class whose objects are exposed to the clients. Please note that it is derived from MarshalByRefObject
.
using System;
namespace MyRemoteService
{
public class MyService: MarshalByRefObject
{
private int countRequests;
public string ServeAll()
{
return "I have received "
+ ++countRequests + " Client requests so far.";
}
}
}
The App.Config file on the server is shown below. See, it has entries for all the three types of objects. All of them are of same class i.e., MyRemoteService.MyService
.
="1.0" ="utf-8"
<configuration>
<system.runtime.remoting>
<application name="MyRemoteService">
<service>
<wellknown
mode="Singleton"
type="MyRemoteService.MyService, MyRemoteService"
objectUri="MySingleton.rem"
/>
<wellknown
mode="SingleCall"
type="MyRemoteService.MyService, MyRemoteService"
objectUri="MySingleCall.rem"
/>
<activated
type="MyRemoteService.MyService, MyRemoteService"
/>
</service>
<channels>
<channel ref="http" port="8989"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
The server is written like this:
using System;
using System.Runtime.Remoting;
namespace MyRemoteServer
{
class RemoteServer
{
[STAThread]
static void Main(string[] args)
{
RemotingConfiguration.Configure("MyRemoteServer.exe.config");
Console.WriteLine("Listening for requests. Press Enter to exit...");
Console.ReadLine();
}
}
}
The client has three member variables defined:
MyService myClientActivatedRem;
MyService mySingleCallRem;
MyService mySingletonRem;
The App.Config on the client is shown below:
="1.0" ="utf-8"
<configuration>
<appSettings>
<add key="MyRemoteSingletonServiceUrl"
value="http://localhost:8989/MyRemoteService/MySingleton.rem"/>
<add key="MyRemoteSingleCallServiceUrl"
value="http://localhost:8989/MyRemoteService/MySingleCall.rem"/>
<add key="MyRemoteClientActivatedServiceUrl"
value="http://localhost:8989/MyRemoteService"/>
</appSettings>
</configuration>
The proxies are created on client load.
private void Client_Load(object sender, System.EventArgs e)
{
string mySingletonUrl = ConfigurationSettings.AppSettings
["MyRemoteSingletonServiceUrl"];
mySingletonRem = (MyService)
Activator.GetObject(typeof(MyService),
mySingletonUrl);
string mySingleCallUrl = ConfigurationSettings.AppSettings
["MyRemoteSingleCallServiceUrl"];
mySingleCallRem = (MyService)
Activator.GetObject(typeof(MyService),
mySingleCallUrl);
string myClientActivatedUrl = ConfigurationSettings.AppSettings
["MyRemoteClientActivatedServiceUrl"];
object[] activateAttribute =
{new UrlAttribute(myClientActivatedUrl)};
myClientActivatedRem =
(MyService) Activator.CreateInstance(typeof(MyService), null,
activateAttribute);
}
The click events of the three buttons have the following respective methods attached:
private void btnSendRequest_Click(object sender, System.EventArgs e)
{
lblSingleton.Text = ((int) ++requestSingletonCount).ToString();
txtSingleton.Text = mySingletonRem.ServeAll();
}
private void btnSendSingleCallRequests_Click(object sender, System.EventArgs e)
{
lblSingleCall.Text = ((int) ++requestSingleCallCount).ToString();
txtSingleCall.Text = mySingleCallRem.ServeAll();
}
private void btnClientActivated_Click(object sender, System.EventArgs e)
{
lblClientActivated.Text = ((int) ++requestClientActivatedCount).ToString();
txtClientActivated.Text = myClientActivatedRem.ServeAll();
}
Few Points to be mentioned
- This article covers only MBR (MarshalByReference). MarshalByObject is not taken into account in this article.
- Recycling of Server-side objects due to Lease-Expiry is not considered to leave the article simpler.
- There are multiple ways to do what has been done. Only the method used in the Sample Application is mentioned in the article.