| Printable Version
.NET REMOTING .. ( A simple approach )
By Shripad Kulkarni
| Source Code : NetRemoting |
|
.NET
Remoting provides a powerful and high performance way of working with
remote objects. Architecturally, .NET Remote objects are a perfect fit
for accessing resources across the network without the overhead posed
by SOAP based WebServices. .NET Remoting is easier to use than Java's
RMI, but definately more difficult than creating a WebService. In this
article, we will create a remote object that will return rows from a database
table. For the sake of simplicity i have used the NorthWind database
that is packed with the installation of the Microsoft SQL Server.
|
|
Developed using the
Release version of Microsoft .NET Visual Studio
|
| Part
I : Creating the Marshal Object .. CustomerLoader |
|
Click
on File->New->Project. Choose to create a new "C# Library" and name it
CustomerInfo then click on OK. This will create the "shared vocabulary"
that both our .NET Remote client and Server will use to communicate. Any
database activity happens on the server side. If you create a separate
object or a class that gets returned from this , you need to declare it
as SERIALIZABLE.
The namespace
we're using for this particular object is CustomerInfo, the object
that is of the type MarshalByRefObject.
A marshaled object allows us to access objects across application domain
boundaries in applications that support remoting.
MarshalByRefObject
is the base class for objects that communicate across application domain
boundaries by exchanging messages using a proxy. Objects that do not inherit
from
MarshalByRefObject
are implicitly marshal by value.
MarshalByRefObject
objects are accessed directly within the boundaries of the local application
domain. The first time an application in a remote application domain accesses
a MarshalByRefObject,
a proxy is passed to the remote application. Subsequent calls on the proxy
are marshaled back to the object residing in the local application domain.
|
| Here is the complete souce listing ... |
using System;
using System.Text;
using System.IO ;
using System.Data.SqlClient;
namespace CustomerInfo
{
public class CustomerLoader : System.MarshalByRefObject
{
private SqlConnection myConnection = null ;
private SqlDataReader myReader ;
public CustomerLoader()
{
Console.WriteLine("New Customer Instance Created");
}
public void Init(string userid , string password)
{
try
{
string myConnectString = "user id="+userid+";password="+password+
";Database=Northwind;Server=SKER;Connect Timeout=30";
myConnection = new SqlConnection(myConnectString);
myConnection.Open();
if ( myConnection == null )
{
Console.WriteLine("OPEN NULL VALUE =====================");
return ;
}
}
catch(Exception es)
{
Console.WriteLine("[Error WITH DB CONNECT...] " + es.Message);
}
}
public void ExecuteSelectCommand(string selCommand)
{
Console.WriteLine("EXECUTING .. " + selCommand);
SqlCommand myCommand = new SqlCommand(selCommand);
if ( myConnection == null )
{
Console.WriteLine("NULL VALUE =====================");
return ;
}
myCommand.Connection = myConnection;
myCommand.ExecuteNonQuery();
myReader = myCommand.ExecuteReader();
}
public string GetRow()
{
if ( ! myReader.Read() )
{
myReader.Close();
return "" ;
}
int nCol = myReader.FieldCount ;
string outstr ="";
for ( int i=0; i < nCol ; i ++)
{
if ( myReader.IsDBNull(i) ) continue ;
outstr+= myReader.GetString(i);
}
return outstr;
}
}
}
|
| Compile this project to generate the CustomerInfo.DLL
to be added to the server and client assemblies. |
| Part II
:Creating the Server |
|
In Visual Studio.NET,
Click on File->New Project. Choose a Command Line Application and name
it CustServer. I have created a simple Windows Application.
You can also play
around by creating a simple console application.
We need to Add Reference
to the CustomerInfo.DLL that will
make use of the Marshal Object. Click on the Project->Add Reference, and
add a reference to the DLL that we created in Part I by clicking the "Browse"
button. In order to use the .NET remote functionality, you must add a
reference to the System.Runtime.Remoting.DLL using Project->Add Reference
under the .NET tab.
|
hts = new HttpServerChannel(8228);
ChannelServices.RegisterChannel(hts);
|
|
Net Remoting supports
2 types of channels . For local applications or ntranetwork you can use
the TcpChannel for better performance. The other type , the HttpChannel
can be used for internet applications In this example I am using the HttpChannel
( just for the heck of it !! ) .If you're working over the internet HTTP
can sometimes be the only choice depending on firewall configurations
This will set up the port number we want our object to respond to requests
on, and the ChannelServices.RegisterChannel will bind that port number
to the stack on the operating system.
|
RemotingConfiguration.RegisterWellKnownServiceType(typeof(CustomerLoader) ,
"CustomerLoader" , WellKnownObjectMode.Singleton);
|
|
The first parameter
is the object you're binding, typeof ( CustomerLoad).
The second parameter is the String that is the name for the object on
the TCP or HTTP channel. For example, remote clients would refer to the
above object as "http://localhost:8228/CustomerLoad".
The third parameter tells the container what should be done with the object
when a request for the object comes in.
WellKnownObjectMode.Single
call makes a new instance of the object for each client WellKnownObjectMode.Singleton
uses one instance of the object for all callers.
It makes sense to
use the Singleton option here since we have to maintain a unique
database connection that can be used across clients and SQL calls.
|
| The complete code for the object is below. |
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime ;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using CustomerInfo;
namespace CustServer
{
///
/// Summary description for Form1.
///
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.TextBox textBox1;
HttpServerChannel hts ;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(292, 273);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textBox1});
this.Name = "Form1";
this.Text = "Form1";
this.TopMost = true;
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
hts = new HttpServerChannel(8228);
ChannelServices.RegisterChannel(hts);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(CustomerLoader) ,
"CustomerLoader" , WellKnownObjectMode.Singleton);
textBox1.Text = "HTTP Channel Registered ...\r\nWaiting on Clients Connect" ;
}
}
}
|
| Compile the Server Object. |
| Part III
:Creating the Client ( CustClient ) |
|
The CustClient
object is our test object of our newly created CustServer remote object.
Create a new project by clicking File->New->Project. I have created a
simple windows client that connects to the remote object , executes an
SQL command and retuns rows from the database as a single string separated
by comma.
Note that we need
to add a reference to our shared CustomerInfo.DLL
created in Part I. Also add reference to the System.Runtime.Remoting DLL
using the References->AddRefrences option in Solution explorer.
Now Notice the 2 important
lines in the program. The first line creates a HTTPClient channel. This
channel is not bound to a port. The seond line actually gets a reference
to our remote CustomerLoad
object. The Activator.GetObject method returns a type of Object that we
can then cast into our CustomerLoad.
The parameters we pass in are extremely similar to what we passed to the
RemotingConfiguration object on the server project. The first parameter
is the Type of the object, the second is the URI of our remote object.
|
ChannelServices.RegisterChannel( new HttpClientChannel());
custl = (CustomerLoader)Activator.GetObject(typeof(CustomerLoader
) , "http://localhost:8228/CustomerLoader");
if ( custl == null )
{
Console.WriteLine("HTTP SERVER OFFLINE ...PLEASE TRY LATER");
return ;
}
custl is an object of type CustomerLoader.
|
| Here is the complete listing ... |
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime ;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using CustomerInfo;
namespace CustomerClient
{
///
/// Summary description for Form1.
///
public class Form1 : System.Windows.Forms.Form
{
CustomerLoader custl ;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.textBox2 = new System.Windows.Forms.TextBox();
this.button2 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Font = new System.Drawing.Fon
t("Verdana", 8.25F, System.Drawing.FontStyle.Bold
, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
this.textBox1.Location = new System.Drawing.Point(0, 32);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(736, 176);
this.textBox1.TabIndex = 0;
this.textBox1.Text = "";
//
// button1
//
this.button1.Location = new System.Drawing.Point(544, 216);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(80, 24);
this.button1.TabIndex = 1;
this.button1.Text = "Run SQL";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(0, 248);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(736, 20);
this.textBox2.TabIndex = 2;
this.textBox2.Text = "";
//
// button2
//
this.button2.Enabled = false;
this.button2.Location = new System.Drawing.Point(632, 216);
this.button2.Name = "button2";
this.button2.Size = new System.Drawing.Size(104, 24);
this.button2.TabIndex = 1;
this.button2.Text = "Get Next Row";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// label1
//
this.label1.Location = new System.Drawing.Point(0, 232);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(192, 16);
this.label1.TabIndex = 3;
this.label1.Text = "Type Your SQL Command Here";
//
// label2
//
this.label2.Location = new System.Drawing.Point(0, 8);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(480, 16);
this.label2.TabIndex = 4;
this.label2.Text = "Data Returned From Remote Object (
Field Values are separated by commas ) ";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(736, 269);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.label2,
this.label1,
this.textBox2,
this.button1,
this.textBox1,
this.button2});
this.Name = "Form1";
this.Text = "CustClient";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
#endregion
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Load(object sender, System.EventArgs e)
{
ChannelServices.RegisterChannel( new HttpClientChannel());
custl = (CustomerLoader)Activator.GetObject(typeof(CustomerLoader
) , "http://localhost:8228/CustomerLoader");
if ( custl == null )
{
Console.WriteLine("HTTP SERVER OFFLINE ...PLEASE TRY LATER");
return ;
}
custl.Init("skulkarni" , "" ) ;
}
private void button2_Click(object sender, System.EventArgs e)
{
textBox1.Text = custl.GetRow() ;
if ( textBox1.Text == "" )
button2.Enabled = false ;
}
private void button1_Click(object sender, System.EventArgs e)
{
button2.Enabled = false ;
textBox1.Text = "" ;
if ( textBox2.Text == "" )
{
MessageBox.Show("Enter a SQL Command" , "Error");
return ;
}
bool ret = custl.ExecuteSelectCommand(textBox2.Text);
if ( ! ret )
{
textBox1.Text = "Error Executing SQL command or 0 rows returned" ;
return ;
}
button2.Enabled = true ;
textBox1.Text = custl.GetRow() ;
}
}
}
|
|
NET remoting makes life very easy for us.
Its very simple to use and can provide an excellent way to work with
resources across your network or the internet.
|
|