Section 10.6. Leasing and Sponsorship


10.6. Leasing and Sponsorship

.NET manages the lifecycle of objects using garbage collection. .NET keeps track of memory allocation and objects accessed by all the clients in the app domain. When an object becomes unreachable by its clients, the garbage collector eventually collects it. If the objects are in the same app domain as the clients, garbage collection functions fine. In fact, even in the case of a client in one app domain accessing an object in a different app domain in the same process, garbage collection still works, because all app domains in the same process share the same managed heap. In the case of remote objects accessed across processes and machines, however, the strategy breaks down because the object may not have any local clients. In this case, if garbage collection were to take place, the garbage collector would not find any references to the object and would deem it garbage, even though there are remote clients (on other machines, or even in separate processes on the same machine) who wish to use the object. The rest of this section addresses this challenge.

In the following discussion, a "remote object" is an object in a different process. The core piece of the .NET remoting architecture designed to address this problem is called leasing and sponsorship. The idea behind leasing is simple: each server object accessed by remote clients is associated with a lease object. The lease object literally gives the server object a lease on life. When a client creates a remote server object (that is, actually creates it, rather than connects to an existing instance), .NET creates a lease object and associates it with the server object. A special entity in .NET remoting called the lease manager keeps track of the server objects and their lease objects. Each lease object has an initial lease time. The clock starts ticking as soon as the first reference to the server object is marshaled across the app domain boundary, and the lease time is decremented as time goes by. As long as the lease time doesn't expire, .NET considers the server object as being used by its clients. The lease manager keeps a reference to the server object, which prevents the server object from being collected in case garbage collection is triggered. When the lease expires, .NET assumes that the server object has no remaining remote clients. .NET then disconnects the server object from the remoting infrastructure. The server object becomes a candidate for garbage collection and is eventually destroyed. After the object is disconnected, any client attempt to access it results in an exception of type RemotingException, letting the client know the object has been disconnected. This may appear strange at first, because the object may very well still be alive. .NET behaves this way because otherwise, the client's interaction with the remote object will be nondeterministic. If .NET allowed remote clients to access objects past their lease time, it would work some of the time but would fail in those cases in which garbage collection had already taken place.

If the remote object is disconnected because the lease has expired, the client can't call any method on it, including IDisposable.Dispose( ). This may have serious scalability consequences. When the object contains expensive resources, make sure to use single-call objectsthese don't require leasing, and .NET calls IDisposable.Dispose( ) on them automatically.


But what about those cases where there are still some remote clients who would like to keep the server object alive after its lease expires? The smart thing to do is to contact such clients and ask them to extend the lease, or, in .NET terminology, to sponsor the lease. Clients that wish .NET to contact them when a server object's lease expires need to provide .NET with a special sponsor object. When the time comes, .NET will contact the sponsor object, giving it a chance to extend the lease. This interaction is illustrated in Figure 10-14.

Figure 10-14. Leasing and sponsorship


The sponsor can extend the lease or refuse to do so. A given server object can have multiple sponsors associated with its lease. The lease manager keeps a list of all the sponsors associated with each lease, and when the lease expires the lease manager starts traversing the sponsor list, looking for a sponsor willing to extend the lease. If such a sponsor is found, the lease manager extends the lease. Otherwise, the lease manager disconnects the server object.

10.6.1. Lease Properties

Every lease has a number of properties associated with it. These properties control the manner in which the lease manager interacts with the remote object's lease. .NET assigns some global default values to these properties, but you can instruct .NET to use other default values. You can even override the default lease properties for individual objects. The expired time is the time that has expired since the beginning of the lease. A lease has a lease time property. By default, if you don't configure it differently, each lease's lease time property is initially set to five minutes. .NET could simply disconnect the object when the expired time is equal to the lease time, but what should it do if clients continue to call the object? This clearly indicates that the object is still useful. Every lease has a renew on call time property; if the lease is about to expire, .NET automatically extends the lease on every call by the value set in the call time renewal property. The default call time renewal is two minutes. The current lease time value (the time the object has to live unless the lease is extended) is a product of the remaining lease time and the renew on call time, according to this formula:

     current lease time = MAX(lease timeexpired time,renew on call time) 

If the renew on call time value is less than the lease time minus the expired time, it will have no effect. The renew on call time has an effect only if it is greater than the lease time minus the expired time. In that case, the expired time property is reset, and the lease time is set to the renew on call time. The result is that even if an object is very busy, its lease time doesn't grow in proportion to the amount of traffic it has. Even if the object has a spike in load, after some quiet time, its lease will expire.

10.6.1.1 Lease manager properties

There are two properties pertaining to the lease manager itself. Obviously, the lease manager needs to monitor the leases of all remote server objects in its app domain. The question is, how often should the lease manager examine the leases? The lease manager's poll time property governs the frequency with which the lease manager polls the leases. The default poll time is set to 10 seconds. The other lease manager property has to do with sponsors. The lease sponsors can reside on remote machines, or it may take them a long time to reach a decision on the lease's fate. The lease manager's sponsorship timeout property controls how long the lease manager should wait for a reply from a sponsor. The sponsorship timeout is required to handle network failures, or even the case of a sponsor machine being down. If the lease manager tries to reach a sponsor and the sponsor doesn't reply within the sponsorship timeout period, the lease manager removes that sponsor from the sponsor list associated with the lease.

10.6.1.2 Configuring global default properties

If you don't like the default values of the various lease and lease manager properties, you can provide your own. You can do so both programmatically and administratively, using the application configuration file. To configure global defaults programmatically, use the static properties of the LifetimeServices class, defined in the System.Runtime.Remoting.Lifetime namespace:

     public sealed class LifetimeServices     {        public static TimeSpan LeaseManagerPollTime { get; set; }        public static TimeSpan LeaseTime            { get; set; }        public static TimeSpan RenewOnCallTime      { get; set; }        public static TimeSpan SponsorshipTimeout   { get; set; }     } 

You typically use these properties in the Main( ) method of your host:

     static void Main( )     {        LifetimeServices.LeaseTime       = TimeSpan.FromMinutes(10);        LifetimeServices.RenewOnCallTime = TimeSpan.FromMinutes(15);               /* Register types or load configuration file */     } 

Note that you must set the global leasing defaults before you register types (programmatically or using the configuration file). The host can start servicing remote calls immediately after registration, and if you haven't already set the new defaults, these initial calls will not use them.

To provide the new global default values in the host configuration file, use the <lifetime> element:

     <configuration>        <system.runtime.remoting>          <application>                  <lifetime               leaseTime = "10M"               sponsorshipTimeOut = "1M"               renewOnCallTime = "15M"               LeaseManagePollTime = "8s"             />          </application>        </system.runtime.remoting>     </configuration> 

10.6.2. Configuring a Lease

Every lease object implements the ILease interface, defined in the System.Runtime.Remoting.Lifetime namespace:

     public interface ILease     {        TimeSpan CurrentLeaseTime   {get;}        LeaseState CurrentState     {get;}        TimeSpan InitialLeaseTime   {get;set;}        TimeSpan RenewOnCallTime    {get;set;}        TimeSpan SponsorshipTimeout {get;set;}        void Register(ISponsor obj);        void Register(ISponsor obj,TimeSpan renewalTime);        TimeSpan Renew(TimeSpan renewalTime);        void Unregister(ISponsor obj);     } 

The ILease interface allows you to control and configure the lease properties for an individual object, as well as to manage sponsors for that lease. Both the object and its clients can obtain the ILease interface. An individual lease can be in one of a number of states; the most important are initial, active, and expired. You can obtain the state of the lease by accessing the CurrentState read-only property of the ILease interface. CurrentState is of the enum type LeaseState:

     public enum LeaseState     {         Active,         Expired,         Initial,         Null,         Renewing     } 

A remote class can provide its own values to the lease's properties, giving the object control over its lifetime. To do so, override the InitializeLifetimeService( ) method defined in MarshalByRefObject and return a lease object. .NET calls InitializeLifetimeService( ) immediately after the remote object's constructor, but before a reference to the object is marshaled back to the client. InitializeLifetimeService( ) is never called if a local client creates the object. Although you can return from InitializeLifetimeService( ) any object that implements the ILease interface, in practice you need to obtain the lease already associated with your object and modify its properties. You do that by calling your base class's InitializeLifetimeService( ) method and modifying the lease properties, as shown in Example 10-10. You can set the lease properties only if the lease is in the LeaseState.Initial state, and this is asserted in the example.

Example 10-10. Providing new lease properties for an object
 public class MyServer : MarshalByRefObject {    public override object InitializeLifetimeService( )    {       ILease lease = (ILease)base.InitializeLifetimeService( );       Debug.Assert(lease.CurrentState == LeaseState.Initial);       //Set lease properties       lease.InitialLeaseTime    = TimeSpan.FromMinutes(30);       lease.RenewOnCallTime     = TimeSpan.FromMinutes(10);       lease.SponsorshipTimeout  = TimeSpan.FromMinutes(2);       return lease;    } } 

10.6.3. Renewing a Lease

Both the object and its clients can extend the lease explicitly by obtaining the object's lease and calling the ILease.Renew( ) method, providing a new lease time. Renewing a lease explicitly affects the current lease time according to this formula:

     current lease time = MAX(lease timeexpired time,renewal time) 

This means that the renewal time will have an effect only if the renewal time is greater than the lease time minus the expired time. In that case, the expired time is reset, and the lease time becomes the renewal time. Consequently, if different clients all try to explicitly renew a lease, the lease will not grow to the value of their combined renewal sum; this ensures that the object remains connected only when clients require it. Both the client and the object obtain the lease associated with the object using the static method GetLifetimeService( ) of the RemotingServices class:

     public static object GetLifetimeService(MarshalByRefObject obj); 

You can renew a lease only if it's in the LeaseState.Active state. For example, here is how a client renews a lease:

     MyClass obj;     obj = new MyClass( );            ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);     Debug.Assert(lease.CurrentState == LeaseState.Active);     lease.Renew(TimeSpan.FromMinutes(30)); 

If the object wants to renew its own lease, it simply calls GetLifetimeService( ), providing itself as the parameter:

     public class MyServer : MarshalByRefObject     {        public void SomeMethod( )        {           ILease lease = (ILease)RemotingServices.GetLifetimeService(this);           Debug.Assert(lease.CurrentState == LeaseState.Active);                  lease.Renew(TimeSpan.FromMinutes(30));           //Do some work        }     } 

10.6.4. Providing a Sponsor

As mentioned already, a sponsor is a third party whom .NET consults when a lease expires, giving that party an opportunity to renew the lease. The sponsor must implement the ISponsor interface, defined as:

     public interface ISponsor     {        TimeSpan Renewal(ILease lease);     } 

The lease manager calls ISponsor's single method, Renewal( ), when the lease expires, asking for new lease time. To add a sponsor to a lease object, simply obtain the lease object by using GetLifetimeService( ) and call the ILease. Register( ) method:

     public class MySponsor : MarshalByRefObject,ISponsor     {        public TimeSpan Renewal(ILease lease)        {           Debug.Assert(lease.CurrentState == LeaseState.Active);           //Renew lease by 5 minutes           return TimeSpan.FromMinutes(5);        }     }     ISponsor sponsor = new MySponsor( );     MyClass obj = new MyClass( );            //Register the sponsor     ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);     lease.Register(sponsor); 

If the sponsor doesn't want to renew the lease, it can return TimeSpan.Zero:

     public TimeSpan Renewal(ILease lease)     {        Debug.Assert(lease.CurrentState == LeaseState.Active);        //Refuse to renew lease:        return TimeSpan.Zero;     } 

However, it probably makes more sense to unregister the sponsor instead. Because the sponsor is called across an app domain boundary, the sponsor must be a remotable object, meaning it must be marshaled either by value or by reference. If you derive the sponsor from MarshalByRefObject, the sponsor will reside on the client's side, and it can base its decision about the renewal time on client-side events or properties that it's monitoring. That raises an interesting question: if the lease keeps the remote server object alive, and the sponsor keeps the lease alive, who keeps the sponsor alive? The answer is that somebody on the client side must keep a reference to the sponsor, typically as a class member variable. Doing so also allows the client to remove the sponsor from the lease when the client shuts down, by calling the ILease.Unregister( ) method (the client can also unregister the sponsor in its implementation of IDisposable.Dispose( )). Unregistering sponsors improves overall performance, because the lease manager doesn't spend time trying to reach sponsors that aren't available.

If you mark only the sponsor as serializable, when you register the sponsor it's marshaled by value to the host's side and will reside there. This eliminates the marshaling overhead of contacting the sponsor, but it also disconnects the sponsor from the client. A marshaled-by-value sponsor can only base its decisions about renewing leases on information available on the host's side.

10.6.4.1 Sponsors and remoting

When the sponsor is a marshaled-by-reference object, the client application must register a port with its channels to allow the remote lease manager to call back to the sponsor, and it must set the type filtering to Full. The client generally doesn't care which port is used for the callbacks, so it can register port number 0 to instruct .NET to automatically select an available port. The channel, port number, and sponsor object location are captured when the reference to the sponsor object is marshaled to the remote host.

10.6.4.2 Client and leases

A single lease can have multiple sponsors, and multiple clients can all share the same sponsor. In addition, a single sponsor can be registered with multiple remote leases. Typically, a client application will have one sponsor for all its remote objects. As a result, a remote object will typically have as many sponsors to its lease as it has distinct client applications using it. When a client application shuts down, it unregisters its sponsor from all the remote leases it sponsors.

10.6.5. Leasing and Remote Activation Modes

Server-activated single-call objects don't need leasing, because such objects are disconnected immediately after each call. This isn't the case for server-activated singleton objects and client-activated objects, though.

10.6.5.1 Leasing a singleton object

The singleton design pattern semantics mandate that once it's created, the singleton object lives forever. Thus, the default lease time of five minutes doesn't make sense for singleton objects. If you don't change this default and no client accesses the singleton object for more than five minutes after it's created (or just two minutes after the first five minutes), .NET deactivates the singleton. Future calls from the clients are silently routed to a new singleton object. .NET supports infinite lease time. When you design a singleton object, override InitializeLifetimeService( ) and return a null object as the new lease, indicating to .NET that this lease never expires:

     public class MySingleton : MarshalByRefObject     {        public override object InitializeLifetimeService( )        {           return null;        }     } 

Returning an infinite lease relieves you of managing global lease defaults or dealing with sponsors. In fact, you can use an infinite lease with any object activation mode, but it makes sense only in the case of a singleton.

10.6.5.2 Leasing a client-activated object

Client-activated objects are the most affected by the leasing mechanism. The only safe way to manage client-activated objects is to use sponsors. All other options, such as setting global lease properties or configuring individual objects' leases, are guesses or heuristics at best. Ultimately, only the client knows when it no longer requires an object. The sponsor should renew each object's lease with an amount of time that on the one hand balances network traffic and load on the lease manager, and on the other hand is granular enough to manage the resources the object may hold. If the sponsor provides too short a renewed lease the lease manager will have to query it frequently to renew the lease, which may result in unnecessary traffic. If the sponsored lease is too long, the lease manager may end up keeping the remote object alive when the client no longer needs it. Every case is unique, and when throughput and performance are a concern, you will have to investigate and profile the system with various sponsorship renewal times. I can, however, offer the following rule of thumb: in general, the sponsorship time should be the same as the initial lease time. The reason is that if the initial lease time is good enough for your casethat is, it doesn't generate too much traffic and isn't too coarseit's likely to be just as suitable for the sponsors on subsequent lease-extension requests.

Example 10-11 demonstrates a client using a client-activated object whose lifetime is controlled by a sponsor. You can use this code as a template or a starting point for your remote client-activated objects. The source code accompanying this book contains Example 10-11 in the Leasing solution. The solution has three projects: the ServerAssembly class library, the RemoteServerHost EXE assembly, and the Client EXE assembly. These projects are very similar to those presented in Example 10-7. The host is identical to the one in Example 10-7; the only difference is in the configuration file. The host registers the MyCAO type as a client-activated object. Because the sponsor is provided to the host in the form of a remote callback object, the host must set its channel's type-filtering level to Full. The rest of the host is omitted from Example 10-11. The ServerAssembly class library contains the MyCAO definition, so both the client and the host can access its metadata. The client assembly contains the MySponsor class, because there is no need to put the sponsor in a class library; all the host needs is the metadata for the ISponsor interface, which is already part of the .NET class libraries. The MySponsor implementation of Renewal( ) simply extends the lease by the initial lease time. The client configuration file designates the MyCAO type as a remote client-activated object, and it registers the port 0 with the channel, as well as elevating type filtering to Full to allow calls back to the sponsor. The client's class (ClientForm) has as member variables both the remote client-activated object and the sponsor. In its constructor, the client creates new instances of the server and the sponsor and registers the sponsor with the lease of the client-activated object. The client is a Windows Forms dialog, with a single button. When clicked, the button simply calls the client-activated object. When the user closes the dialog, the client unregisters the sponsors.

Example 10-11. Sponsoring a client-activated object
 /////////////  RemoteServerHost.exe.config : the host configuration file  //////// <?xml version="1.0" encoding="utf-8"?> <configuration>   <system.runtime.remoting>     <application>       <service>          <activated  type="RemoteServer.MyCAO,ServerAssembly"/>       </service>       <channels>          <channel ref="tcp" port="8005">             <serverProviders>                <formatter ref="soap"   typeFilterLevel="Full"/>                <formatter ref="binary" typeFilterLevel="Full"/>             </serverProviders>          </channel>       </channels>      </application>   </system.runtime.remoting> </configuration> /////////////////////   ServerAssembly class library  //////////////////////////// namespace RemoteServer {    public class MyCAO : MarshalByRefObject    {       int m_Counter = 0;       public void Count( )       {          m_Counter++;          string appName = AppDomain.CurrentDomain.FriendlyName;          MessageBox.Show("Counter value is " + m_Counter,appName);       }         } } ////////////////  Client.exe.config: the client configuration file  ////////////// <?xml version="1.0" encoding="utf-8"?> <configuration>    <system.runtime.remoting>       <application>          <client url="tcp://localhost:8005">             <activated  type="RemoteServer.MyCAO,ServerAssembly"/>          </client>          <channels>             <channel ref="tcp" port="0">              <serverProviders>                   <formatter ref="soap" typeFilterLevel="Full"/>                   <formatter ref="binary" typeFilterLevel="Full"/>                </serverProviders>             </channel>          </channels>       </application>    </system.runtime.remoting> </configuration> ///////////////////////////  Client EXE assembly  //////////////////////////////// using System.Runtime.Remoting.Lifetime;    public class MySponsor : MarshalByRefObject,ISponsor {    public TimeSpan Renewal(ILease lease)    {       Debug.Assert(lease.CurrentState == LeaseState.Active);          return lease.InitialLeaseTime;    } }    using RemoteServer;    partial class ClientForm : Form {    Button m_CallButton;       ISponsor m_Sponsor;    MyCAO m_MyCAO;       public ClientForm( )    {       InitializeComponent( );          m_Sponsor = new MySponsor( );       m_MyCAO = new MyCAO( );          //Register the sponsor       ILease lease = (ILease)RemotingServices.GetLifetimeService(m_MyCAO);       lease.Register(m_Sponsor);    }    void InitializeComponent( )    {...}       static void Main( )    {       RemotingConfigurationEx.Configure( );          Application.Run(new ClientForm( ));    }    void OnCall(object sender,EventArgs e)    {       m_MyCAO.Count( );    }    void OnClosed(object sender,EventArgs e)    {       //Unegister the sponsor       ILease lease = (ILease)RemotingServices.GetLifetimeService(m_MyCAO);       lease.Unregister(m_Sponsor);    } } 

10.6.6. Sponsorship Management

The sponsor in Example 10-11 is a completely general-purpose sponsor that simply returns the initial lease time (whatever that may be) on every renewal request. The problem with the code shown in Example 10-11 is that the client has to keep track of its remote objects and manually unregister each remote lease's sponsor when the application shuts down. However, you can automate this by providing a client-side sponsorship manager, in the form of the SponsorshipManager[*] helper class:

[*] I first presented SponsorshipManager in the December 2003 issue of MSDN Magazine.

     public class SponsorshipManager : MarshalByRefObject,                                       ISponsor,                                       IDisposable,                                       IEnumerable<ILease>     {        public void Dispose( );        public TimeSpan Renewal(ILease lease);        public IEnumerator<ILease> GetEnumerator( );        public void Register(MarshalByRefObject obj);        public void Unregister(MarshalByRefObject obj);        public void UnregisterAll( );             public void OnExit(object sender,EventArgs e);     } 

Example 10-12 shows the implementation of SponsorshipManager, which is available with the source code of this book.

Example 10-12. The SponsorshipManager helper class
 public class SponsorshipManager : MarshalByRefObject,                                   ISponsor,                                   IDisposable,                                   IEnumerable<ILease> {    IList<ILease> m_LeaseList = new List<ILease>( );    ~SponsorshipManager( )    {       UnregisterAll( );    }    void IDisposable.Dispose( )    {       UnregisterAll( );    }    TimeSpan ISponsor.Renewal(ILease lease)    {       Debug.Assert(lease.CurrentState == LeaseState.Active);       return lease.InitialLeaseTime;    }    IEnumerator<ILease> IEnumerable<ILease>.GetEnumerator( )    {       foreach(ILease lease in m_LeaseList)       {          yield return lease;       }    }    IEnumerator IEnumerable.GetEnumerator()    {       IEnumerable<ILease> enumerable = this;       return enumerable.GetEnumerator()    }    public void OnExit(object sender,EventArgs e)    {       UnregisterAll( );    }    public void Register(MarshalByRefObject obj)    {       ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);       Debug.Assert(lease.CurrentState == LeaseState.Active);       lease.Register(this);       lock(this)       {          m_LeaseList.Add(lease);       }    }    public void Unregister(MarshalByRefObject obj)    {       ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);       Debug.Assert(lease.CurrentState == LeaseState.Active);       lease.Unregister(this);       lock(this)       {          m_LeaseList.Remove(lease);       }    }    public void UnregisterAll( )    {       lock(this)       {          while(m_LeaseList.Count>0)          {             ILease lease = m_LeaseList[0];             lease.Unregister(this);             m_LeaseList.RemoveAt(0);          }       }    } } 

The ClientSponsor Class

.NET provides a class for sponsorship management called ClientSponsor, defined as:

     public class ClientSponsor : MarshalByRefObject,ISponsor     {        public ClientSponsor( );        public ClientSponsor(TimeSpan renewalTime);        public TimeSpan RenewalTime{get;set;}        public void Close( );        public bool Register(MarshalByRefObject obj);        public virtual TimeSpan Renewal(ILease lease);        public void Unregister(MarshalByRefObject obj);     } 

ClientSponsor is similar in its design and use to SponsorshipManager, but unlike with SponsorshipManager, which returns the initial lease timeout for each lease, with ClientSponsor the client must explicitly set a single fixed lease renewal timeout for all sponsored leases. Because of this behavior, I recommend that you use SponsorshipManager instead of ClientSponsor.


SponsorshipManager implements ISponsor by returning the initial lease time of the provided lease. In addition, it maintains in a member variable called m_LeaseList a generic linked list of all the leases it sponsors. SponsorshipManager provides the Register( ) method:

     public void Register(MarshalByRefObject obj); 

Register( ) accepts a remote object, extracts the lease from it, registers SponsorshipManager as the sponsor, and adds the lease to the internal list. The Unregister( ) method of the SponsorshipManager class is defined as:

     public void Unregister(MarshalByRefObject obj); 

This method removes the lease associated with the specified object from the list and unregisters SponsorshipManager as a sponsor. SponsorshipManager also provides the UnregisterAll( ) method:

     public void UnregisterAll( ); 

This method unregisters SponsorshipManager from all the lease objects in the list. Note that the list access is done in a thread-safe manner by locking the SponsorshipManager on every access. Thread safety is required because multiple clients on multiple threads can use the same SponsorshipManager instance to manage their remote leases. However, the clients should not bother themselves with calling Unregister( ) or UnregisterAll( )SponsorshipManager provides an event-handling method called OnExit( ):

     public void OnExit(object sender,EventArgs e); 

OnExit( ) calls UnregisterAll( ) as a response to the client application shutting down. Example 10-13 shows how to use SponsorshipManager by a Windows Forms client.

Example 10-13. Using SponsorshipManager
 partial class ClientForm : Form {    Button m_CallButton;    SponsorshipManager m_SponsorshipManager;    MyCAO m_MyCAO; //The remote CAO    public ClientForm( )    {       InitializeComponent( );       m_SponsorshipManager = new SponsorshipManager( );       m_MyCAO = new MyCAO( );       m_SponsorshipManager.Register(m_MyCAO);       Application.ApplicationExit += m_SponsorshipManager.OnExit;    }    void InitializeComponent( )    {...}    static void Main( )    {       RemotingConfigurationEx.Configure( );       Application.Run(new ClientForm( ));    }    void OnCall(object sender,EventArgs e)    {            m_MyCAO.Count( );    } } 

The client in Example 10-13 maintains as member variables a remote object of type MyCAO (defined in Example 10-11) and a SponsorshipManager object. The client registers the remote object with SponsorshipManager and hooks up the application OnExit event with the SponsorshipManager OnExit( ) event-handling method. SponsorshipManager does the restit registers itself as the sponsor, and it unregisters all the remote leases it sponsors automatically when the client shuts down. Finally, note the use of C# 2.0 iterators[*] in implementing the IEnumerable<ILease> interface. The interface allows clients to iterate over the list of managed sponsors, if the need ever arises:

[*] If you are unfamiliar with C# 2.0 iterators, see my article "Create Elegant Code with Anonymous Methods, Iterators, and Partial Classes" in the May 2004 issue of MSDN Magazine.

     SponsorshipManager sponsorshipManager = new SponsorshipManager( );     //Some code to initialize sponsorshipManager, then:     foreach(ILease lease in sponsorshipManager)     {...} 



Programming. NET Components
Programming .NET Components, 2nd Edition
ISBN: 0596102070
EAN: 2147483647
Year: 2003
Pages: 145
Authors: Juval Lowy

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