CMXtraneous

Right on the edge of useful

Persistent State CFCs and CFQUERY

Posted Thursday, June 30, 2005 5:39:51 PM by Tom Muck

Tom Muck

I wrote this in my article on persistent CFCs for Community MX last week, but it's worth repeating here because I see a lot of people using CFCs in session and application scope that do not take this into account: you should always declare local variables at the top of your <cffunction> tag:

<cffunction name="blah" returntype="any">
<cfset var i = 0>
<cfloop from="0" to="10" index="i">
<!--- Some code --->
</cfloop>
</cffunction>

Many people are doing this, but I wonder how many people apply the same principle to recordsets within the CFQUERY tag. . . .I see this a lot:

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

If this is in a persistent scope, the variable rs will be available even after the return call. In fact, it will hang around for the life of the persistent CFC. To properly scope the query, you should declare it first:

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

Now, the rs query will be destroyed after the function returns the variable to the caller -- it is not persisted within the CFC. You can try it like this. Create a cfc:

<cfcomponent>

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

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

</cfcomponent>

The function testRSBad() looks like it should throw an error, because rs is not defined, however if this is in persistent state and you hit the testRS() method first, then rs is persisted for the entire session.

Try it out: make sure you have sessions turned on in the Application.cfm file. Then put some code on a page called testrs.cfm:

<a href="testrs.cfm?hit=true">Next page</a>

<cfif not isdefined("url.hit")>
  <cfset session.user1 = createobject("component","testuser").new()>
  <cfset session.user1.testRS()>
</cfif>

<cfdump var=#session.user1.testRSBad()#>

You have a link to the page, and you are instantiating the session instance of the CFC only once -- when you preview the page. When you hit the link, you will see the page again with the same recordset dumped out, but this time it is coming from the leftover persisted global variable rs that was not scoped properly in the CFC.

Cross-posted at Tom-Muck.com

Category tags: ColdFusion, Community MX