Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Design Patterns for ASP.NET Developers, Part 3: Advanced Patterns  : Page 2

Find out how to use Factory, Builder, and Injection Patterns in ASP.NET to construct different representations of complex objects.

Implementing the Publish-Subscribe Pattern
The example application demonstrates the Publish-Subscribe pattern by exposing a custom event from the publisher, a class named EventProductionLine, through an associated helper class named BuildCarHelper. The custom event exposes an instance of the BuildCarEventArgs class as its second argument (all these classes are located in the App-Code folder of the downloadable sample code). The BuildCarEventArgs class exposes properties that reflect the requirements of the application, and of the notification that the production line must send to subscribers. In this simple example, the two properties are the model name of the new car, and the date and time it was built:

public class BuildCarEventArgs : EventArgs { // custom arguments class for BuildCarEvent. This simple // implementation exposes only the model name and build date, // but could be extended to add any other values/data private String argsModel; private DateTime argsDate; public BuildCarEventArgs(String modelName, DateTime buildDate) { // save model name and build date argsModel = modelName; argsDate = buildDate; } public String ModelName { // public property to expose car model name get { return argsModel; } set { argsModel = value; } } ... public DateTime BuildDate { // public property to expose build date get { return argsDate; } set { argsDate = value; } } }

The BuildCarHelper class publishes an event named BuildCarEvent, which it raises when the production line builds a new car. The code first declares the BuildCarEvent event handler. Note that its BuildCarEventArgs argument uses C# Generics syntax (to use this syntax, you must include in your class a reference to the System.Collections.Generics namespace):

public class BuildCarHelper { // event that this Helper class exposes to subscribers public event EventHandler<BuildCarEventArgs> BuildCarEvent; ...

The helper class exposes a method named Notify that a publisher can call to notify all subscribers when it builds a new car. The code in this method first extracts any values it requires from the EventProductionLine instance passed to it, and then creates an instance of the BuildCarEventArgs class with the appropriate values. Finally, it calls a separate method that raises the event through the .NET event system using the approach recommended by Microsoft that avoids a race condition if a subscribed class instance un-subscribes while the code is executing:

public void Notify(Object publisher) { // raise the event to notify all subscribers using // the custom event arguments class String carName = (publisher as EventProductionLine).CurrentMotorCarName; BuildCarEventArgs args = new BuildCarEventArgs( carName, DateTime.Now); // call method to publish event using standard // approach defined by MS for events - see .NET docs at // http://msdn2.microsoft.com/en-us/ // library/w369ty8x(VS.80).aspx OnBuildCarEvent(args); } // wrap event invocations inside a protected virtual method // to allow derived classes to override the event invocation // behavior protected virtual void OnBuildCarEvent(BuildCarEventArgs e) { // make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber un-subscribes // immediately after the null check and before the event // is raised. EventHandler<BuildCarEventArgs> handler = BuildCarEvent; // event will be null if there are no subscribers if (handler != null) { // use the () operator to raise the event. handler(this, e); } }

The following code shows the EventProductionLine class in the example application. It creates an instance of the BuildCarHelper class that it will use to raise the BuildCarEvent, and exposes it as a public property. Exposing the helper instance as a property of the EventProductionLine class means that the client application can reference the helper to subscribe to any events it exposes—in this simple example just the BuildCarEvent:

public class EventProductionLine { // holds a reference to an instance of the helper class that // raises the event to notify any subscribers private BuildCarHelper internalHelper = null; // name of car currently being built private String carName = String.Empty; public EventProductionLine() { // class constructor // get new helper class instance internalHelper = new BuildCarHelper(this); } public BuildCarHelper Helper { // property that exposes the helper instance get { return internalHelper; } }

The publisher will usually expose other methods that the helper class can access as it creates the custom BuildCarEventArgs instance. In this example, the only other property exposed by the EventProductionLine class returns the name of the car it is currently building.

The remaining code is the BuildNewMotorCar method that the client will call when it wants the production line to build a specific model of car—as specified by the parameter to this method. In the method, the code saves the name of the car in a local variable so that the CurrentMotorCarName property can expose it, does whatever work is required to build the car, and then calls the Notify method of the associated BuildCarHelper instance to raise the BuildCarEvent to all subscribed clients:

// any properties required to expose data that the // subscribers may need to use. These property values // must be wrapped inside the custom args class that the // Helper uses when raising the event public String CurrentMotorCarName { get { return carName; } } // main method for class&#151;this is called by // the subject to accomplish the main tasks. There can // be more methods, but each must call the // Notify or other equivalent method of the helper to // raise an event public void BuildNewMotorCar(String motorCarName) { // save value of car name carName = motorCarName; // do whatever work required here // ... // notify all subscribers internalHelper.Notify(this); } }

The subscriber implementations in the example application maintain a Hashtable in the user's session that contains a count of each new car built by the current instance of the EventProductionLine class. In the constructor of each subscriber class, code looks for a Hashtable using the relevant session key declared in a local String constant at the start of the class file, and stores this Hashtable in a local variable. If there is no stored Hashtable, the code creates an empty one. As an example, here's the relevant code from the EventPartsDept class:

public class EventPartsDept { Hashtable partsUsed = null; const String SESSION_KEY_NAME = "SavedPartsCount"; public EventPartsDept() { // class constructor, get saved Hashtable from // session if available if (HttpContext.Current.Session[SESSION_KEY_NAME] != null) { partsUsed = (Hashtable) HttpContext.Current.Session[SESSION_KEY_NAME]; } else { // create new empty Hashtable partsUsed = new Hashtable(); } }

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date