I’m a relative novice using ColdFusion’s ORM features having done just one “real” project so far that took advantage of it. I’m working on an application that needs to be able to set the datasource for each request based on the URL that the customer is using to access the site. For example, if a customer visits http://customera.demoapp.com I need the application to use the DSN named “dsn-customera”, http://customerb.demoapp.com gets “dsn-customerb” and so on.

Normally this wouldn’t be an issue–you’d do some sort of logic in the onRequestStart() method in Application.cfc and set a request or session scoped variable to the name of the DSN and go from there. However, when using ColdFusion’s ORM functions, this approach doesn’t work since “this.datasource” is configure in the pseudo-constructor at the top of Application.cfc like so:

1
2
<cfset this.name = "myApp" />
<cfset this.datasource = "dsn-myapp" />

The first way that I thought of solving this was to set this.datasource=”demo” in the pseudo-constructor and then overwrite the value in the onRequestStart() method like so:

1
2
3
4
5
6
7
8
9
10
11
12
<cfset this.name = "myApp" />
<cfset this.datasource = "dsn-myapp" />
 
<cffunction name="onRequestStart" output="false" >
 
    <cfset local.calculatedDSN = "" />   
 
    <!--- Logic to determine correct DSN name for current customer here --->
 
   <cfset this.datasource = local.calculatedDSN />
 
</cffunction>

This did in fact override the value in this.datasource as evidenced by dumping the “this” scope at the start and end of the onRequestStart() method. However, CF’s ORM system always used the value set in the pseudo-constructor even though the this.datasource value was changed successfully. After scratching my head for a while, I posted a message to the cf-orm-dev Google group with what I was trying to do and the results I was seeing.

Turns out, I was on the right track, I just had my logic in the wrong place. After exchanging a few messages with Brian Kotek, Sumit Verma, and Andrew Scott, I learned that CF’s ORM system is configured before any of the event handlers specified in Application.cfc are fired.

In the end, the answer to what I was needing to do was to put a call to a custom, private function in the pseudo-constructor to set up the customer-specific DSNs and then CF’s ORM system would be configured dynamically on each each request for the correct customer database. The only other gotcha is that you have to have a unique application name set up for each customer in order for things to be cached correctly. Attempting to set this.datasource per request while using a single application name resulted in all requests using the datasource specified by the logic on the first request to the application.

The code ended up looking something similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<cfcomponent output="false">
 
    <!--- Application settings --->
    <cfset setupCustomerValues() />
 
    <!--- Normal Application.cfc event handler methods --->
 
    <cffunction name="setupCustomerValues" output="false" access="private" 
       returntype="void" hint="">
 
       <cfset local.customerName = "" />
       <cfset local.customerDSN = "" />
 
        <!--- Logic to determine customer-specific values here --->
 
        <cfset this.name = "myapp-#local.customerName#" />
        <cfset this.datasource = local.customerDSN />
        <!--- more settings declarations --->
    </cffunction>

This is obviously demo/proof-of-concept/blog-brevity type code so there are a couple of things to note:

  • This method creates a separate ColdFusion application in memory for every client. If you have a large number of objects that are created and cached at application startup and a large number of customers hitting the same server, you could/will run into memory issues. In my case, this was less of a problem than having to deploy a completely separate code base for each customer using the application.
  • The demo code doesn’t take into account any kind of caching of the results of the customer-specific logic. Obviously in a production application, you wouldn’t want to execute that logic on every request, but cache the result after the first execution for that customer (in my example, the first request to a particular subdomain)

6 thoughts on “Dynamically set ColdFusion ORM datasource

  1. @Russ Actually, I didn’t try that. That might bear some experimentation to see if that will work correctly. Off the top of my head, I don’t see any reason why it wouldn’t. The only down side that I can see (and it’s admittedly a small one) is that request.myDSN is spread wide over your code base, but that might be acceptable if you didn’t have to have separate CF applications running for each customer.

    I’ll play around with that tonight or tomorrow and report back.

  2. @Russ – It should be noted that the datasource attribute to the component is for mixing your entities across multiple databases, and was only added into ColdFusion 9.01

    The method described here, is for ColdFusion 9.0 and provides the way to change the entire ORM datasource not on a per entity basis.

    Can I say that this is an extremly powerful little tid bit.

  3. Just had a need to do this same sort of thing (setting the datasource based on environment). Don’t know that I would figured out the need (heck, even the ability) to do that kind of constructor function without this blog post.

Leave a Reply

Your email address will not be published. Required fields are marked *

*