Saving and Retrieving the User's State
Now that we know what we need to save, let's code the GetState and SaveState methods that actually read the write the data values. This PublishersWebUI sample application will save the state data for each user into a simple CSV text file in which the filename is based on the UserID property value. I like using CSV files during initial development of Web applications because it's simple to change what gets written to them, and because it's a simple enough matter to open them with Notepad to make sure that everything is being saved correctly. After your application design stabilizes (maybe I should say if it ever stabilizes), you might consider changing it to instead write the data to a binary file or to a table in a relational database.
The logic to save the state information is very simple:
Open a CSV file for output destroying the previous contents if it already exists.
Write the search filter value to a record.
Write the number of selected authors in the collection to a record. This simplifies retrieving them later.
For each author in the collection, write the Publisher ID, Publisher Name, and Publisher Phone.
The source code for the SaveState method looks like this:
' Saves all state information to a simple CSV file. Public Sub SaveState() Dim lFileNbr As Integer Dim lPubState As clsPubState lFileNbr = FreeFile Open StateFileName For Output As lFileNbr ' Save the state data. Write #lFileNbr, Filter Write #lFileNbr, Publishers.Count For Each lPubState In Publishers Write #lFileNbr, lPubState.PubID, lPubState.Name, lPubState.Phone Next lPubState Close lFileNbr End Sub
Both the SaveState and GetState methods use the StateFileName Private Property procedure:
Private Property Get StateFileName() As String StateFileName = App.Path & "\" & mUserID & ".usf" End Property
This property simply returns the fully qualified filename for the state file. In my example, I'm creating the state filename by simply suffixing the UserID property value with .usf (User State File) and saving it to the same folder as where the component is installed. In reality, the path for the state files should be configurable from an INI file option or a registry setting.
For clsUserState to restore the state information, the GetState method generally works like the SaveState method in reverse. However, there are a couple of exceptions to handle. The first one is that we should expect that sometimes the state file won't exist. This could happen because of the following:
This is the first request from the user and we haven't written any state information yet.
The state file has expired and been deleted by our external process.
The uid query string value has been altered by the user somehow. In this case, the uid query string value will be used to create the state filename as long as it contains valid characters that can be used when naming a file. If it contains invalid characters, the operating system will raise a run-time error, so it might be a good idea to generate a new UserID value if the file cannot be opened.
In all cases, if the file doesn't exist, there is no state information to restore so the Filter and Publishers properties should contain default values.
The second exception to check is to make sure that even if a state file is found, it doesn't have expired data in it. Comparing the modified date/time of the state file with the current system time easily does this. The expiration period can be whatever you wantminutes, hours, and even days or months. If the difference is greater than whatever you want the expiration period to be, simply ignore the contents of the file, and again the Filter and Publishers properties should contain default values. In my example, I assume the state data has expired if the file hasn't been used in the last 60 minutes.
My completed GetState method looks like this:
' Attempt to restore the state information from the CSV file. ' If the state file is not found, or the data in the file hasn't ' been used in the last 60 minutes, then we assume a new session. Public Sub GetState() Dim lFileNbr As Integer Dim lNbrPubs As Long Dim lIndex As Long Dim lID As Long Dim lName As String Dim lPhone As String Dim lPubState As clsPubState Filter = "" Set Publishers = New Collection lFileNbr = FreeFile On Error Resume Next Open StateFileName For Input As lFileNbr 'If file not found, then that means no state information exists. If Err.Number = 53 Then ' 53 = File Not Found. Exit Sub ElseIf Err.Number <> 0 Then Call Err.Raise(Err.Number, Err.Source, Err.Description) End If On Error GoTo 0 ' Reset error handling so run-time errors get raised. ' If state information is > 60 minutes old, then ignore it. If DateDiff("n", FileDateTime(StateFileName), Now) > 60 Then Close lFileNbr Exit Sub End If ' Get the state data. Input #lFileNbr, Filter Input #lFileNbr, lNbrPubs For lIndex = 1 To lNbrPubs Step 1 Set lPubState = New clsPubState ' Note the syntax of the Input statement won't allow us to input ' directly into an object's properties so instead we have to input ' into local variables and then assign to the object. Input #lFileNbr, lID, lName, lPhone lPubState.PubID = lID lPubState.Name = lName lPubState.Phone = lPhone Call Publishers.Add(lPubState) Next lIndex Close lFileNbr End Sub