A Safer Way to Use TempVars (1 Viewer)

Pat Hartman

Super Moderator
Staff member
Local time
Today, 02:58
Joined
Feb 19, 2002
Messages
43,720
I forgot to ask, does the suggested method work when using the Access Runtime engine or even just an .accde?
 

ebs17

Well-known member
Local time
Today, 08:58
Joined
Feb 7, 2020
Messages
2,022
Somehow that sounds to me like programming that only deals with itself in order to deal with itself.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
I forgot to ask, does the suggested method work when using the Access Runtime engine or even just an .accde?
I do not think it was intended that way, but as a development tool. It is just an easy way to use extensibility to create the properties. I do not think the intent would ever be to run this at runtime.
 

GPGeorge

George Hepworth
Local time
Yesterday, 23:58
Joined
Nov 25, 2004
Messages
2,088
Here I go again, believing what Microsoft documentation says.


The description of the "value" argument to the TempVars.Add method says:



IF you can use other data types, great... but that isn't what is advertised. I like to avoid stretching the boundaries on things.
Here's the chart in that reference:

NameRequired/OptionalData typeDescription
NameRequiredStringThe name to use for the TempVar.
ValueRequiredVariantThe value to store as a TempVar. This value must be a string expression or a numeric expression. Setting this argument to an object data type will result in a run-time error.

Note that it states, in column three, which labeled "Data type", that the data type of the value in a TempVar is a variant, whereas the data type of its name is a string.

Tempvars accept strings or numerics as input, but it is stored as a variant. This is a problem in some cases, such as where I pass dates to them or booleans. My solution is converting values retrieved from TempVars to other datatypes as appropriate before using them.

I use the functions above to convert them as needed, e.g.
Code:
SELECT DT.diabetestestid,
       DT.testdate,
       DT.testtime,
       DT.testtypeid,
       DT.testtimeofdayid,
       DT.testresult,
       DT.comments,
       tbldiabetestesttype.sortorder
FROM   tbldiabetestests AS DT
       INNER JOIN tbldiabetestesttype
               ON DT.testtypeid = tbldiabetestesttype.diabetestesttypeid
WHERE  DT.testdate >= Tempvarsdate("dtfromdate")
           AND DT.testdate <= Tempvarsdate("dttodate") )
         AND  Iif(Tempvarslong("lngtesttypeid") = 0, 0, [DT].[testtypeid])
               IN ( 0,   Tempvarslong("lngtesttypeid") )
ORDER  BY DT.testdate DESC,  tbldiabetestesttype.sortorder;

This SQL has three TempVars, two dates and a long integer. I acknowledge that in many cases, an integer would work as well as a long integer.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
@KitaYama
I modified the code to do away with any tempvars and instead build class variables. I also included the ability to reference class objects. Now it can be done, but I would highly caution about setting an object. I set a form as one of my myTempVars. But a form or a control is only a pointer to the open instance. If the form or control would close then that MyTempVar is also terminated.

Here is my table.
tblMyTempVars tblMyTempVars

MyTempVarIDVarNameVarTypeName
1​
Product_NameString
2​
Start_DateDate
3​
PaymentCurrency
4​
Default_FormForm
When I run the code it creates my class
Code:
' ---===== DO NOT EDIT DIRECTLY =====---
' This class module is auto-generated; to recreate run:
'  Generate MyTempVars tblMyTempVars

Option Compare Database
Option Explicit


Private m_Default_Form As Form
Private m_Payment As Currency
Private m_Product_Name As String
Private m_Start_Date As Date


Public Property Set Default_Form(NewValue As Form)
   Set m_Default_Form = NewValue
End Property

Public Property Get Default_Form() As Form
   Set Default_Form = m_Default_Form
End Property

Public Property Let Payment(NewValue As Currency)
    m_Payment = NewValue
End Property

Public Property Get Payment() As Currency
   Payment = m_Payment
End Property

Public Property Let Product_Name(NewValue As String)
    m_Product_Name = NewValue
End Property

Public Property Get Product_Name() As String
   Product_Name = m_Product_Name
End Property

Public Property Let Start_Date(NewValue As Date)
    m_Start_Date = NewValue
End Property

Public Property Get Start_Date() As Date
   Start_Date = m_Start_Date
End Property

Notice the use of class variables and no tempvars.

I wrote the following function by hand, but could have simply used extensibility to build the function. This is ONLY Needed for use in a query.

Code:
Public Function GetTempVar(VarName As String) As Variant

' ONLY USED FOR QUERIES. DO NOT USE ANYWHERE ELSE IT DEFEATS THE PURPOSE

  Select Case VarName
  Case "Product_name"
    GetTempVar = MyTempVars.Product_Name
  Case "start_Date"
    GetTempVar = MyTempVars.Start_Date
  Case "Payment"
    GetTempVar = MyTempVars.Payment
  Case "Default_Form"
    'SINCE A QUERY CANNOT HAVE A FORM OBJECT POINTER, I RETURN THE NAME here.
    GetTempVar = MyTempVars.Default_Form.Name
  End Select
End Function

Try adding additional "TempVars" to the table.
 

Attachments

  • MyTempVarsDemo.accdb
    1.6 MB · Views: 55
Last edited:

KitaYama

Well-known member
Local time
Today, 15:58
Joined
Jan 6, 2022
Messages
1,621
@KitaYama
I modified the code to do away with any tempvars and instead build class variables. I also included the ability to reference class objects. Now it can be done, but I would highly caution about setting an object. I set a form as one of my myTempVars. But a form or a control is only a pointer to the open instance. If the form or control would close then that MyTempVar is also terminated.

Here is my table.
tblMyTempVars tblMyTempVars

MyTempVarIDVarNameVarTypeName
1​
Product_NameString
2​
Start_DateDate
3​
PaymentCurrency
4​
Default_FormForm
When I run the code it creates my class
Code:
' ---===== DO NOT EDIT DIRECTLY =====---
' This class module is auto-generated; to recreate run:
'  Generate MyTempVars tblMyTempVars

Option Compare Database
Option Explicit


Private m_Default_Form As Form
Private m_Payment As Currency
Private m_Product_Name As String
Private m_Start_Date As Date


Public Property Set Default_Form(NewValue As Form)
   Set m_Default_Form = NewValue
End Property

Public Property Get Default_Form() As Form
   Set Default_Form = m_Default_Form
End Property

Public Property Let Payment(NewValue As Currency)
    m_Payment = NewValue
End Property

Public Property Get Payment() As Currency
   Payment = m_Payment
End Property

Public Property Let Product_Name(NewValue As String)
    m_Product_Name = NewValue
End Property

Public Property Get Product_Name() As String
   Product_Name = m_Product_Name
End Property

Public Property Let Start_Date(NewValue As Date)
    m_Start_Date = NewValue
End Property

Public Property Get Start_Date() As Date
   Start_Date = m_Start_Date
End Property

Notice the use of class variables and no tempvars.

I wrote the following function by hand, but could have simply used extensibility to build the function. Now I can use my tempvars in a query.

Code:
Public Function GetTempVar(VarName As String) As Variant
  Select Case VarName
  Case "Product_name"
    GetTempVar = MyTempVars.Product_Name
  Case "start_Date"
    GetTempVar = MyTempVars.Start_Date
  Case "Payment"
    GetTempVar = MyTempVars.Payment
  Case "Default_Form"
    GetTempVar = MyTempVars.Default_Form.Name
  End Select
end function

Try adding additional "TempVars" to the table.
@MajP I sincerely appreciate your effort on this.
Your warning is noted. I'll do some tests

Million thanks.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
@MajP I sincerely appreciate your effort on this.
Your warning is noted. I'll do some tests
The concepts are interesting, but unless you are doing this a lot you could probably build the class from scratch just as fast as building the table and assembling the other code into a project.
 

Uncle Gizmo

Nifty Access Guy
Staff member
Local time
Today, 07:58
Joined
Jul 9, 2003
Messages
16,418
The extra advantage of this technique is that during testing, I can make the form visible so I can easily watch values change and I can even help with testing to change the value on the fly to make the code take a different path so it becomes easier to test multiple scenarios.

I like!
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 14:58
Joined
May 7, 2009
Messages
19,249
Try adding additional "TempVars" to the table.
on your table definition you define Default_Form as Form, yet the function is only returning a string (it's name). you could have used String as VartypeName to "conform" to the definition. what you just made is a violation of your own definition.
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 14:58
Joined
May 7, 2009
Messages
19,249
i don't see other benefit of using Tempvars in a Class.

As a programmer who created the Tempvars, you should document in paper all variables of the system
including Tempvars. That is the old skool practice. Same documents is submitted/ turnover to the client when
the programming project is completed, together with other documentations.

see this simple class that uses tempvars:
Code:
VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "TV"
Attribute VB_Description = "Tempvar Class"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

Dim m_strName As String

Public Sub Add(ByVal strName As String, Optional ByVal varValue As Variant = Null)
m_strName = strName
TempVars.Add strName, varValue
End Sub

Public Property Get Value() As Variant
Attribute Value.VB_UserMemId = 0
Value = TempVars(m_strName).Value
End Property

Public Property Let Value(ByVal p_value As Variant)
Attribute Value.VB_UserMemId = 0
TempVars(m_strName).Value = p_value
End Property


Private Sub Class_Terminate()
TempVars.Remove m_strName
End Sub


using a test sub:

Code:
Private Sub test()

    Dim t As New TV
    
    t.Add "my_tempvar", 100
    
    Debug.Print t 'result=100
    Debug.Print TempVars("my_tempvar").Value  'result=100
    
    t = 20
    
    Debug.Print t 'result=20
    Debug.Print TempVars("my_tempvar").Value 'result=20
    
End Sub
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
on your table definition you define Default_Form as Form, yet the function is only returning a string (it's name). you could have used String as VartypeName to "conform" to the definition. what you just made is a violation of your own definition.. The function was only used to demo the query. No real way to use a form in a query so I put the name. Again the point is notqqe
No you missed the point! You need the read the whole thread. I clearly demonstrate that it supports objects and state that the function is only to use MyTempVars in a query.
Code:
Private Sub cmdSet_Click()
  'LOOK HERE FOR AN OBJECT being used

  Set MyTempVars.Default_Form = Forms!frmTest

  MyTempVars.Payment = 55#
  MyTempVars.Start_Date = Date
  MyTempVars.Product_Name = "Chai"
End Sub

Private Sub cmdProve_Click()

'***************************  MYTEMPVAR returning an Object
  Me.txtOut = "Start Date:" & MyTempVars.Start_Date & vbCrLf & " Default Form: " & MyTempVars.Default_Form.Name
End Sub


You have to read the whole thread, but obviously you did not so I annotated it. The only purpose of the function is for use in a query, and you could return the name of the form or just do away with it in the query because unlikely you would ever need that

Code:
Public Function GetTempVar(VarName As String) As Variant

' ONLY USED FOR QUERIES. DO NOT USE ANYWHERE ELSE IT DEFEATS THE PURPOSE

  Select Case VarName
  Case "Product_name"
    GetTempVar = MyTempVars.Product_Name
  Case "start_Date"
    GetTempVar = MyTempVars.Start_Date
  Case "Payment"
    GetTempVar = MyTempVars.Payment
  Case "Default_Form"

    'SINCE A QUERY CANNOT HAVE A FORM OBJECT POINTER, I RETURN THE NAME. DO NOT BE CONFUSED
    GetTempVar = MyTempVars.Default_Form.Name
  End Select
End Function
 
Last edited:

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
i don't see other benefit of using Tempvars in a Class
Well then you missed the whole point of this entire thread. It is not meant for the client it is meant for you the developer.
Again. With this method you get
  • Visibility
  • Type safety
  • IntelliSense
  • Compile-time checks for typos
So much easier to develop with these features.
 

KitaYama

Well-known member
Local time
Today, 15:58
Joined
Jan 6, 2022
Messages
1,621
I just wanted to add because I prefer to be explicit in data type, I added 2 more functions. For now, all my tempvars are either string, Boolean or integer

Code:
Public Function GetTempVar_bool(VarName As String) As Boolen
.....
Public Function GetTempVar_int(VarName As String) As Integer
.....
Public Function GetTempVar_str(VarName As String) As String
.....
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
I just wanted to add because I prefer to be explicit in data type, I added 2 more functions. For now, all my tempvars are either string, Boolean or integer

Code:
Public Function GetTempVar_bool(VarName As String) As Boolen
.....
Public Function GetTempVar_int(VarName As String) As Integer
.....
Public Function GetTempVar_str(VarName As String) As String
.....
I see no need for that, if we are talking the same approach as I demoed. Again, you are only using the function in a query and you will not get any benefit from that over a single function.
Again you should be using the class in your code and getting type safety and only the function for queries.

My single function will return the correct sub type
To demo that variants are cast automatically to correct type.

Code:
Public Sub TestSubType()
  Dim x As Variant
  x = 1
  Debug.Print VarType(x)
  x = 0.99
  Debug.Print VarType(x)
  x = Date
  Debug.Print VarType(x)
End Sub
Returns
Code:
2
5
7
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 14:58
Joined
May 7, 2009
Messages
19,249
You have to read the whole thread, but obviously you did not so I annotated it.
obviously you are using Class to save the Object(form) and not in tempvars variable.
you can just use a simple class without the use of tempvars.
 

MajP

You've got your good things, and you've got mine.
Local time
Today, 02:58
Joined
May 21, 2018
Messages
8,685
obviously you are using Class to save the Object(form) and not in tempvars variable.
you can just use a simple class without the use of tempvars
You are going to have to read the whole long thread. That is entirely missing the point of the demo and the whole conversation. The point is the code generates the class and yes it does not use tempvars. We know that already.
 

KitaYama

Well-known member
Local time
Today, 15:58
Joined
Jan 6, 2022
Messages
1,621
My single function will return the correct sub type
To demo that variants are cast automatically to correct type.
I was distracted by the following comment in TV class added by Mike Wolf in his code. (at top of GetVar function)

' By using Variants for the default value and return type, we give up some
' type safety for the convenience of having a single function

Thanks again for your sample database.
 

Users who are viewing this thread

Top Bottom