12.10 Fire an Event Over a Remoting Channel


Problem

You need to create a client that can receive an event fired by a remote object.

Solution

Make sure you are using bidirectional channels. Create a remotable object on the client-side that can receive the event from the server.

Discussion

Although the event-handling syntax doesn't change when you use .NET Remoting, it takes additional steps to create a client that can handle an event from a remote object. Here are the key requirements:

  • The remotable class must use client-activated or singleton activation mode (not single-call). This ensures that the object remains alive in between method calls, allowing it to fire an event to the client.

  • The client must use a bidirectional channel so that it can receive connections initiated by the server.

  • The EventArgs object for the event must be serializable so that it can be transmitted across application domain boundaries.

  • The client must use a remotable "listener" object to receive the event. This listener will then raise a local event that can be handled by the client. The remote object can't fire the event directly to an ordinary class because ordinary classes aren't accessible from other application domains.

  • You must modify the client and server configuration files to explicitly allow full serialization. (This isn't required with .NET 1.0.)

Here's a sample remotable class that you might use to fire an event to the client. It provides a single public method StartTask . This method starts a timer, which fires after a short delay (about 10 seconds). When the timer fires, the remotable object raises a TaskComplete event.

 using System; using System.Timers; public delegate void TaskCompleted(object sender,   TaskCompleteEventArgs e); public class RemoteObject : MarshalByRefObject {     public event TaskCompleted TaskComplete;     private Timer tmr = new Timer();     public void StartTask() {         tmr.Interval = 10000;         tmr.Elapsed += new ElapsedEventHandler(tmrCallback);         tmr.Start();     }     private void tmrCallback(object sender, ElapsedEventArgs e) {         tmr.Enabled = false;         if (TaskComplete != null) {             TaskComplete(this,                new TaskCompleteEventArgs("Task completed on server"));         }     }     public override object InitializeLifetimeService() {         return null;     } } [Serializable()]  public class TaskCompleteEventArgs : EventArgs {     public string Result;     public TaskCompleteEventArgs(string result) {         this.Result = result;     } } 

The next step is to define a remotable class that runs on the client and can receive this event. This class can then contact the client. The EventListener class shown in the following code provides one such exampleit simply raises a second event, which the client can handle directly. As with all remotable objects, it will only be accessible remotely for five minutes, unless you explicitly modify the lifetime lease policy (as described in recipe 12.11). One approach is to simply override the InitializeLifetimeService method to allow the object to live forever, as shown here:

 public class EventListener : MarshalByRefObject {     public event RemoteObject.TaskCompleted TaskComplete;     // Handle the remote event.     public void OnTaskComplete(object sender,        RemoteObject.TaskCompleteEventArgs e) {         // Now raise the event to a local listener.         TaskComplete(sender, e);     }     public override object InitializeLifetimeService() {         return null;     } } 

The event listener must be defined in a separate assembly so that it can be referenced by the client application and the remotable class, which both need to interact with it.

Now the client application can start the asynchronous task through the RemoteObject class and handle the event through the EventListener . The following form code shows a simple client that displays a message box when the event is received.

 using System; using System.Windows.Forms; using System.Runtime.Remoting; public class ClientForm : System.Windows.Forms.Form {     private System.Windows.Forms.Button cmdStart;     // (Designer code omitted.)     RemoteObject.RemoteObject remoteObj;     EventListener.EventListener listener;     private void ClientForm_Load(object sender, System.EventArgs e) {         // Create the remote object and remotable listener.         RemotingConfiguration.Configure("Client.exe.config");         remoteObj = new RemoteObject.RemoteObject();         listener = new EventListener.EventListener();     }     private void cmdStart_Click(object sender, System.EventArgs e) {         // Connect the remotable event handler.         remoteObj.TaskComplete += new            RemoteObject.TaskCompleted(listener.OnTaskComplete);         // Connect the local event handler.         listener.TaskComplete += new RemoteObject.TaskCompleted(TaskComplete);         remoteObj.StartTask();         MessageBox.Show("Task has been started.");     }       // Define the local event handler.     private void TaskComplete(object sender,       RemoteObject.TaskCompleteEventArgs e) {         // This event fires on one of the Remoting listener threads.         MessageBox.Show("Event received: " + e.Result);     } } 

For this to work, you must make sure that the client is using bidirectional channels. Thus, the channel tag in the configuration files should look like this:

 <channel  ref="tcp"  port="0" /> 

And not like either of these examples:

 <channel  ref="tcp server"  port="0" /> <channel  ref="tcp client"  port="0" /> 

In addition, you must explicitly enable support for full serialization. Otherwise, the server will not be allowed to receive a delegate for the Listener.TaskCompleted method, and it won't be able to connect the remote event handler. To enable full serialization support on the server, you need to modify the component host configuration file as shown here:

 <configuration>   <system.runtime.remoting>     <application>              <client url="tcp://localhost:9080/Server">         <activated type="RemoteObject.RemoteObject, RemoteObject"/>       </client>              <channels>         <channel ref="tcp" port="0">           <serverProviders>              <formatter ref="binary" typeFilterLevel="Full" />            </serverProviders>          </channel>        </channels>            </application>   </system.runtime.remoting> </configuration> 

To enable full serialization support on the client, you need to modify the client configuration file, as shown in the following code:

 <configuration>   <system.runtime.remoting>     <application name="SimpleServer" >              <service>         <activated type="RemoteObject.RemoteObject, RemoteObject"/>       </service>                          <channels>         <channel ref="tcp" port="9080">           <serverProviders>              <formatter ref="binary" typeFilterLevel="Full" />           </serverProviders>          </channel>        </channels>             </application>   </system.runtime.remoting> </configuration> 



C# Programmer[ap]s Cookbook
C# Programmer[ap]s Cookbook
ISBN: 735619301
EAN: N/A
Year: 2006
Pages: 266

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net