The Singleton design pattern has its place in the desktop environment where there will be only one user running the application, and when that user quits the app the Singleton is destroyed. This doesn’t fit the requirements of a multi-user environment such as the web where many users will be accessing the application at once. I recently came across this issue when porting a desktop app to the web which required me to design a Singleton class that is session sensitive. Here’s a walk through of how I achieved my goal. Please note that scopes are particularly important when creating this class.
If you need a refresher on the Singleton design pattern, read Design Patterns in Xojo: Singleton.
First, I inserted a class into my project and called it SessionSingleton (if you don’t name your class SessionSingleton you need to replace all references to that name in your class).
Next, I added a property to that class with the name SessionIdentifier As String with no default value and set as private scope. This will hold the Session.Identifier associated with this object.
Protected Property SessionIdentifier as String
Now add a method called Constructor and make it private, this is to prevent the object from being created outside control of the class.
Private Sub Constructor() End Sub
Add another method called Constructor with a parameter SessionIdentifier As String and make it private. In this method add the code:
Protected Sub Constructor(SessionIdentifier As String) Me.SessionIdentifier = SessionIdentifier Constructor() End Sub
This saves the Session Identifier and runs the constructor method (which may be used by you to initialize properties etc. of the object).
Now, add a shared Property mInstances() As SessionSingleton and make it private. This will hold the array of session Singletons.
Private Shared Property mInstances() as SessionSingleton
Lastly, we need to add two public Shared Methods. The first is:
Public Shared Function GetInstance(SessionIdentifier As String) as SessionSingleton // Find the instance. If it exists return it. For i As Integer = 0 To mInstances.LastRowIndex If mInstances(i).SessionIdentifier = SessionIdentifier Then Return mInstances(i) End If Next // Not found so create a new instance and return it mInstances.AddRow(New SessionSingleton(SessionIdentifier)) System.DebugLog("Creating new instance for Session with ID " + SessionIdentifier) Return mInstances(mInstances.LastRowIndex) End Function
This code returns an existing instance or creates and returns a new one.
Now, because a Web application is multi-user app there’s the need to clean up when a session is closed so we need the second Shared Method:
Public Shared Sub DestroyInstance(SessionIdentifier As String) // This function will remove the instance and should be called from the // Session.Closing event For i As Integer = 0 To mInstances.LastRowIndex If mInstances(i).SessionIdentifier = SessionIdentifier Then mInstances(i) = Nil mInstances.RemoveRowAt(i) End If Next End Sub
In this code we find the instance that has the identifier of the current session and destroy that object.
To use the class you would:
Var Singleton As SessionSingleton = SessionSingleton.GetInstance(Session.Identifier)
Or if in a Session Event Handler:
Var Singleton As SessionSingleton = SessionSingleton.GetInstance(Identifier)
Add the code to the Session.Closing Event handler to clean up.
SessionSingleton.RemoveInstance(Identifier)
Don’t forget to replace all references to SessionSingleton to your class name.
You may have noticed I haven’t excluded an empty string as the SessionIdentifier and indeed have defaulted the constructor to this empty string. This allows me to use this same class in my desktop projects and potentially when handling Special URL’s in a web project.
Wayne Golding has been a Xojo developer since 2005 and is a Xojo MVP. He operates the IT Company Axis Direct Ltd which primarily develops applications using Xojo that integrate with Xero www.xero.com. Wayne’s hobby is robotics where he uses Xojo to build applications for his Raspberry Pi, often implementing IoT for remote control.