Global variables, while traditionally frowned upon in favor of tightly scoped variables, have their place. Globals make for great data transport mechanisms between classes, and can reduce trips to the database server if you're looking for the same information in several places.
The problem is that ASP.NET is a stateless environment. Unlike Windows Forms programming, you don't have a big pool of memory that you can go to for any variable that you need, wherever you need it. Once a page is done executing, the memory is dumped, and you can't access anything that you've done. Similarly, where do you temporarily store things you're working on during page execution, especially if you're skipping into and out of classes?
The solution ASP.NET offers is some memory spaces which are manifested as if they were dictionaries / hash tables / whatever. Basically, you'll need to explicitly create an entry in a particular memory space to store your variable. One quick example here: Session("bob") = "billy" -- this will store your variable just fine.
I needed a variable available to me to share data with other business classes, but I wanted to dispose of it once the page was done executing. I didn't want the same user to get the same variable, likewise I didn't want to persist the variable across the entire Application. All the articles I was reading online ALMOST had it right, but veered off course in one direction or the other, hence this post.
Here are some questions and answers about where you can store your variables given different scenarios:
- Where do I store a variable that I only need during the page's execution, but I need it across different classes / controls?
- In a class: HttpContext.Current.Items("Item") = "My Item" (can be an instantiated instance of a class, or nearly anything else).
- In a page: Current.Items("Item") = "My Item" (same memory space as above, this is the shortcut)
- Where do I store a constant or a function that I need in several places?
- Public Shared - Function, Sub, and Property allow you to set a variable / procedure once, and have it available without setting an instance before using it. (example, you name your class "bob" and your method "tony" and you can say in any page or class "bob.tony"). This is the same concept as a "static" function. There are issues with this method: You can't use "Private" variables in your functions, as you'll never have an instance of your class. While this makes your variables / functions quickly available, it doesn't allow you to directly modify their contents, and you'll execute a function fresh every time. There may be a way around this last caveat, but I don't know what that way may be - if you happen to know, please post a comment.
- Module - if you code "Public Module" instead of "Public Class", it's the same exact thing as making a bunch of Public Shared functions within your Class.
- You can also put constants into various config files, see below.
- Where do I store a variable that I need through multiple page executions, for one user, on the same ASPX?
- Use the ViewState.
- Use a Literal control with Visible = False
- Where do I store a variable that I need for one user only, possibly across multiple pages?
- This is where you'd use Session or Cookies. Cookies scale a bit better than Session variables, because you're not asking the server to hold onto data. However, you can scale Sessions using SQL Server, so keep that in mind, especially if security is an issue.
- In a class: Conext.Current.Session("Item") = "My Item"
- Where do I store a variable that I need for every user of the application?
- The Application space is one place. Application("Item") is still available, for backwards compatibility with ASP.
- A better construct is Cache. Cache allows you to set up a lot of little triggers that will expire the variable, from strict time to keep it alive, to weird conditions, like the existence of a file!
- Where do I store constants to be available throughout the application, like connection strings?
- Web.config
- Where do I store constants to be available throughout the entire SITE, including subordinate applications, like connection strings?
- Web.config - web.config files cascade, so your application's web.config can override your site's web.config, BUT that doesn't mean that your work in the site's web.config has gone to waste.
- Where do I store constants to be available across an entire machine? Or - I'm getting errors when I cluster web servers, because the ViewState becomes invalid when I switch to a different machine in the cluster... why?
- machine.config sets constants that are global across an entire machine. One of the more common problems in creating web clusters is as above - the encryption key is different between machines, and because of this, one machine's ViewState can't be used on another machine in the cluster. machine.config is where you set your machinekey, and solve this problem.
Here's a diagram of the relationships you'll see between different spaces you can place variables and constants:
Here's a sample implementation of a couple of global variables:
Public Class Globals
#Region "Page Scope Variables - HttpContext"
'''
''' Gets the current myInfo object. If there is none, then create a new one.
'''
'''
'''
'''
Public Shared Property CurrentInfo() As myInfo
Get
If Not IsNothing(HttpContext.Current.Items("Info")) Then
Return CType(HttpContext.Current.Items("Info"), myInfo)
Else
Dim myInfo As New myInfo()
myInfo.SubIWantToRunWhichSetsExtraThingsUpInMyClass()
HttpContext.Current.Items("Info") = myInfo
HttpContext.Current.Items("InfoRefreshedAt") = Now.ToString
Return CType(HttpContext.Current.Items("Info"), myInfo)
End If
End Get
Set(ByVal value As myInfo)
HttpContext.Current.Items("Info") = value
End Set
End Property
#End Region
#Region "Session Scope Variables - Session"
'''
''' Gets the current ClientInfo object
'''
'''
'''
'''
Public Shared Property CurrentClient() As ClientInfo
Get
If Not IsNothing(HttpContext.Current.Session("Client")) Then
Return CType(HttpContext.Current.Session("Client"), ClientInfo)
Else
Dim theClient As New ClientInfo()
HttpContext.Current.Session("Client") = theClient
HttpContext.Current.Session("ClientPostedAt") = Now.ToString
Return CType(HttpContext.Current.Session("Client"), ClientInfo)
End If
End Get
Set(ByVal value As ClientInfo)
HttpContext.Current.Session("Client") = value
End Set
End Property
#End Region
End Class
And here in a page, I'm testing for a value which exists in each of the classes, and am also checking WHEN that variable was last populated. In this example, you'll see Literal2 repopulate with each refresh, and Literal4 won't refresh until you begin a new session.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim thisInfo As myInfo = Globals.CurrentInfo
Literal1.Text = thisInfo.Variable1
Literal2.Text = Context.Items("InfoRefreshedAt").ToString
Dim thisClient As ClientInfo = Globals.CurrentClient
Literal3.Text = thisClient.IPAddress
Literal4.Text = Session("ClientPostedAt").ToString
End Sub
When you're done messing around with code, check out some information about NES Classics. There's over 800 NES games listed.

No comments:
Post a Comment