Raymond Camden's Blog Rss

Find the bug....

16

Posted in ColdFusion | Posted on 06-22-2007 | 1,814 views

I was helping a user yesterday who ran into an odd problem. His CFC was using Application.DSN (which is another problem) and he kept getting an error that said Application.DSN didn't exist. He copied his line that defined Application.DSN and set it in the Request scope. When he updated the CFC to use the Request scope, it worked fine. What was the problem? Here is some code that simulates his Application.cfm. See if you can spot it before reading on....

view plain print about
1Application.cfm:
2<cfset application.dsn = "goo">
3<cfset request.dsn = "goo">
4<cfapplication name="myapp">
5<cfset application.random = "Paris Hilton is my idol....">
6
7test.cfm:
8<cfdump var="#application#">
9<cfdump var="#request#">

If you run this test, you will clearly see that the Application scope only has one variable, random. The Request scope shows the DSN. So now you can see what he saw - that the Application scope was working incorrectly and the Request scope worked fine.

But... look again. Do you see his CFAPPLICATION tag? It is after his application.dsn line. ColdFusion had not "started" the Application yet. Therefore his DSN variable was actually added to the magical unnamed Application scope area. To see what I mean, simply add a dump before his CFAPPLICATION tag.

So two lessons from this:

1) First, always remember to put the CFAPPLICATION tag before any code that actually uses the Application scope.

2) Second, this is one more big reason to migrate to Application.cfc. If he had used onApplicationStart(), this would not/could not have happened.

Comments

[Add Comment] [Subscribe to Comments]

Ray,
I totally agree with you here. There is no need to be referencing application.dsn inside of your cfc. I wrote an article about avoiding global data in this case. http://www.danvega.org/blog/index.cfm/2007/2/19/Av... . The other thing wrong with that code is that Paris hilton should be nobody's Idol :-)
I'm thinking it's because he defined the Application.name AFTER setting the Application.DSN
Oops, the [more] button got me.. :D
So the "problem," as you put it, is not necessarily using an application.dsn, but rather referencing it inside of a component. You had me worried there for a moment. ;)
Right - but that really wasn't the main point of the entry. The real bug was putting app vars before the cfapp tag.
Well sure, but I saw that pretty quickly.
For those of us who are OO newbies, can you say more about why we shouldn't reference application.dsn in a CFC and what we should do instead?
David: This isn't an OO thing. It is an encapsulation thing. The idea is that you won't your code to know as little as possible. Here is a very simple, trivial example. Imagine you want to write a function that will say hello to you. So if my name is Ray, it should say:

"Hello, Ray!"

I could write my CFC to do this:

return "Hello, " & session.name

And that works fine - but note the use of session.name. The CFC assumes that the session scope is enabled and that the name key stores my name. If I stop using the session scope or switch to a different key (or keys, maybe client.firstname and client.lastname), then my CFC suddenly stops working.

But if my CFC took an argument, name, and did this:

return "Hello, " & arguments.name

It now works no matter what scope my name is stored in.
Um... Ok. But:

What version of CF can you start to use app.cfc? MX7?

And Dan - in your example - you show that:
<cfargument name="dsn" type="string" required="true">
<cfset variables.dsn = arguments.dsn>
and then in the CFC query you use datasource="#variables.dsn#"

- - Which I understand how it works - but, why is that better? Because of memory use?? Isn't the idea to only set one variable once? (especially if it doesn't change?)

I thought the idea of "application." variables was that if it is "app" wide - you only have to set it once and then it's done. In your example - we would continually be setting that value for each CFC in the App... Which seems redundant to a fault. So - what am I missing? Future Flexibility?
Being somewhat new to CF I'm usually quite puzzled when you post these little problems and hide the answer behind the more button. Upon seeing the problem I feel like a complete idiot and I wonder how I missed something so obvious. Well, I'm happy to say that I saw this one right off the bat. And it was pretty exciting! :D
Thanks for the response Ray.

I realize I'm digressing wildly from the point of this post, so feel free to stop me-- I'm just thinking out loud. I have a lot of Coldfusion 5 code that's in the process of migrating to MX, so this is basically a new problem to me. I've never had to worry about passing datasources into functions because in addition to no CFCs, CF5 doesn't have CFFUNCTION and therefore you can't run a query in a UDF. I'm used to setting application.dsn once and being able to assume that all of the code in the application can use it.

Having to include a DSN argument in every function you create sounds like a lot of work. Further, I would worry about consistency-- some functions would have the DSN first, some would have it last. Would it make sense to have a singleton datasource object that is inherited by other objects in the app? Is there a convention for how this problem is handled in Coldfusion OO code?
Another option with CFCs is to use an init function and just set the dsn (and other variables) there.

<cffunction name="init" returntype="any" output="false">
<cfargument name="dsn" required="true">

<cfscript>
this.dsn = arguments.dsn;
</cfscript>

<cfreturn this />
</cffunction>


and then in your code you just call the init function.


<cfscript>
products = createobject("component","cfc.name").init(application.dsn);
<cfscript>

and within the CFC you would use this.dsn for datasource name.

CFCs are the way to go IMHO.
@David: CF5 did have UDFs - they were just script based. It also had custom tags.

Encapsulation sometimes DOES mean more work. But we code for stronger code, not for 'getting it done as fast as possible.' ;)

As for having a consident "place" for DSN - don't forget you can always used named params:

foo = myfuncorcfc.function(name="ray", dsn="#application.dsn#", etc=foo)
Nick - see simplico. THe idea is to set it once when you create the CFC.
Ray, I use UDFs in CF5 all the time. My point is that in CF5, UDFs can only be created in cfscript. Unless there's a trick I'm unaware of, this means they can't run queries and therefore don't have much use for a data source name.

simplico's answer works for me... thanks simplico!
I misread your comment David - I thought you were implying there were no UDFs in CF5.

[Add Comment] [Subscribe to Comments]