What are the advantages and disadvantages of putting CFC instances into Session or Application variables? There have been a lot of discussions about this, but my own personal view is that you can reduce the strain on system resources and memory, and build faster applications by storing CFC instances in persistent variables.

Application variables are stored in memory, and only changed if the server is restarted, or the application variable times out. Usually, you would set your application variable timeout to be a day, a week, or a month in the future. This is because you don't want application variables to be frequently updated -- they are in the Application state because of this. Your <cfapplication> tag in the Application.cfm file should look something like this:

<cfapplication name="blah"
sessionmanagement="yes"
sessiontimeout=#createtimespan(0,0,20,0)#
applicationtimeout=#createtimespan(7,0,0,0)#
clientmanagement="no">

I usually set my application variables to time out after a week to a month, and sessions after about 20 minutes. What does this mean for CFCs?

Ok, lets say you have a CFC that supplies user interface elements. For example, a dropdown list of states/provinces can be hard-coded, or it can come from a database. If you store the CFC that supplies the interface elements in an application variable, the CFC methods are now in memory and the object does not have to be instantiated each time a user hits the page. Sure, the CFC consumes memory, but only ONCE. If the same CFC was not stored in persistent memory, your application would have an instance for each user that hits the page. Not only is the instance consuming memory, but it is taking time to instantiate. 20 users on your site would equal 20 instances of the CFC in your server memory.

Session state is useful also, for certain things. A User object, for example, is a good CFC to store in a session variable. Your user object would only need to be instantiated once per session rather than once per page, which would take more time. Once in memory, the object can store properties as well as methods, so that the user login status, shopping cart info, email address, or other user data is readily available. Calling methods on the CFC is quicker because the object is already in memory. Also, I like to break classes into distinct entities, so a User object would contain only user properties, but one of those properties might be a Cart object, instantiated as a separate object if needed.

A typical CFC instantiation is done like this, using a regular non-persistent variable:

<cfset myUserObj = CreateObject("component", "User")>

Using persistent state variables, you would invoke it like this:

<cflock type="readonly" scope="session" timeout="10">
  <cfif NOT isdefined("Session.myUserObj")>
    <cfset _userNotSet = true />
  </cfif>
</cflock>

<cfif IsDefined("_userNotSet")>
  <cflock type="exclusive" scope="session" timeout="10">
    <cfset Session.myUserObj = CreateObject("component", "User").new()>
  </cflock>
</cfif>

Or simillarly for an Application scope CFC instance:

<cflock type="readonly" scope="application" timeout="10">
  <cfif NOT isdefined("Application.UI")>
    <cfset _userInterfaceNotSet = true />
  </cfif>
</cflock>

<cfif IsDefined("_userInterfaceNotSet")>
  <cflock type="exclusive" scope="application" timeout="10">
    <cfset Application.UI = CreateObject("component", "UI").new()>
  </cflock>
</cfif>

Note the use of locking when addressing the Session and Application scopes. Since a readonly lock takes less resources than an exclusive lock, I use a readonly lock for checking the existance of the variable, and an exclusive lock only if the variable does not exist yet, which will happen once per session for a session variable or once per restart or timeout on an application variable.

Typically, I'll set up a method in the CFC called init() or new() to act as a constructor -- the method simply returns the CFC instance, like so:

<cfcomponent>
   <cffunction name="new" access="public" returntype="User" output="false">
     <cfset this.UserID = CreateUUID() />
     <cfset this.loggedIn = false />
     <cfreturn this />
  </cffunction>
</cfcomponent>

I set the returntype to User, which is the name of the CFC (return types can be any valid type, including CFC instances). Usually, I will use <cfscript> to keep the code more concise, especially with multiple properties:

<cfcomponent>
   <cffunction name="new" access="public" returntype="User" output="false">
     <cfscript>
     this.UserID = CreateUUID();
     this.loggedIn = false;
     return this;
     </cfscript>
  </cffunction>
</cfcomponent>

With that in mind, I structure my CFCs as self-contained objects, with their own properties and methods. Instead of calling a CFC method to login a user and passing the username/password as arguments, I set them as properties and then simply invoke the CFC:

<cfset Session.myUserObject.username = form.username>
<cfset Session.myUserObject.password = form.password>
<cfset Session.myUserObject.login()>

This brings up other issues of scoping of variables and locks. The best practice in any CFC code is to scope all method variables as local by using the var keyword. You should not do this:

<cffunction name="login" access="public" output="false">
  <cfquery name="rs" datasource=#this.datasource#>
    SELECT UserID FROM mytable
    WHERE username = '#this.username#'
    AND password = '#this.password#'
  </cfquery>
  <cfif rs.RecordCount GT 0>
    <cfset time = Now()>
    <cfquery name="rs" datasource=#this.datasource#>
     UPDATE mytable SET LastLogin = '#time#'
     WHERE UserID = #rs.UserID#
    </cfquery>
  </cfif>
</cffunction>

The variable time is created as global variable within the CFC because it was not defined with var. The variable will be accessible by every function in your CFC, and also persists for the entire session, which is probably not desirable. Instead, define your variables.

<cffunction name="login" access="public" output="false">
  <cfset var time = "">
  ...etc

Now, the variable is only available to the function, which is much safer, and destroyed when the function is finished. The variable declarations have to be at the top of the function, or you will see an error. Also, make sure you apply this process to ALL variables, even variables within queries and other ColdFusion constructs. This is a typical CFC method call that will create a global recordset within the CFC instance:

<cfcomponent>
  <cffunction name="testRS" access="public" output="false">
    <cfquery name="rs" datasource="Northwind">
    SELECT * FROM Products
    </cfquery>
  </cffunction>

  <cffunction name="testRSReturn" access="public" output="false">
    <cfreturn rs>
  </cffunction>
</cfcomponent>

If you call the first method, there is no return value, but the recordset rs is created. Upon calling the second method (testRSReturn()), the recordset still exists and will be returned.

<cfset session.user = createobject("component","User").new()>
<cfset session.user.testRS()>
<cfdump var=#session.user.testRSReturn()#>

This can result in some strange results if you don't realize that the recordset is created globally:

<cffunction name="login" access="public" output="false">
  <cfquery name="rs" datasource="#this.datasource#">
    SELECT UserID FROM Users
    WHERE username = '#this.username#' and password = '#this.password#'
  </cfquery>
  <cfreturn rs.UserID >
  </cffunction>

<cffunction name="searchProducts" access="public" output="false">
  <cfargument name="search" type="string">
  <cftry>
  <cfquery name="rs" datasource="#this.datasource#">
    SELECT * FROM Products WHERE ProductName LIKE '#search#'
  </cfquery>
  <cfcatch>
    <cfmail to="#this.admin#" from="#this.admin#" subject="Error">
    There was an error in searchProducts
    </cfmail>
   </cfcatch>
  </cftry>  
  <cfreturn rs>
</cffunction>

In this case, if the login() method was called first, and the searchProducts() method was called and the query triggered an error, the login recordset would be returned from the searchProducts method. Sure, bad coding, but you get the idea of what the dilemma is -- the rs recordset exists for the entire session as a global variable within the object. Define the variables first to eliminate the problem:

<cffunction name="testRS" access="public" output="false">
  <cfset var rs = "">
  <cfquery name="rs" datasource="Northwind">
  SELECT * FROM Products
  </cfquery>
  <cfreturn rs>
</cffunction>

<cffunction name="testRSReturn" access="public" output="false">
  <cfreturn rs>
</cffunction>

Now, the second function would return an error because the rs variable was declared as local to the testRS() method.

CFC properties can be set up in the CFC with a <cfproperty> tag, or simply set in the new() or init() method, as I showed in the constructor function. Locking should be done when writing to the CFC properties, and optionally when reading them (although it is a good practice to always lock them). Locking is tricky, and can be done in a number of ways, however one way is to set up the locking within the CFC function, so that your web application does not have to worry about it, and use setter and getter functions to access all properties:

<cffunction name="setUsername">
  <cfargument name="username" type="string">
  <cflock name="#createuuid()#" type="exclusive" timeout="10">
    <cfset this.username = username />
  </cflock>
</cflock>

Now, when you need to set the username property of your user object, set it like this:

<cfset Session.user.setUsername("tom") / >

Similarly, when retrieving a property, you would use a getter method so that the lock can be applied:

<cffunction name="getUsername">
  <cfset var _username = "" />
  <cflock name="#createuuid()#" type="readonly" timeout="10">
    <cfset _username = this.username / >
  </cflock>
  <cfreturn _username />
</cflock>

And call it like this to display the property:

<cfoutput>#Session.user.getUsername()#</cfoutput>

How important are locks? That is debatable, as you will only run into race conditions requiring locking on rare occasions, however it is better to be safe than sorry.

Conclusion

Persistent state variables are great for CFC instances, but can also be a little tricky to implement properly. Follow a few simple rules to keep them safe and reliable.