当这些变量用于DataSource和数据库模式时,如何避免在CFC中使用SESSION变量?

时间:2011-07-13 15:42:32

标签: coldfusion scope session-variables cfc application-variables

我正在尝试重构我的所有CFC以避免使用SESSION和APPLICATION变量(这不是一项简单的任务)。

但是,在此应用程序中,SESSION变量用于每个数据库调用,因为不同的登录用户可能正在访问不同的数据库和模式:

<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
    SELECT *
    FROM #SESSION.DatabaseSchema#.Employees
</cfquery>

我不想经历将这两个SESSION变量传递给访问数据库的每个方法调用的麻烦。特别是这种情况,因为我不想在远程AJAX调用中传递DSN和模式名称。

执行此操作的最佳做​​法是什么 - 对于不应在CFC中使用的所有范围?

5 个答案:

答案 0 :(得分:2)

我认为既然数据源确实是变量的,我会将它作为可选参数传递给每个函数,并将默认值设置为变量scoped dsn属性。我在CFC的构造函数中设置变量范围的DSN。这样你只需要传入DSN来进行AJAX调用。

<cffunction name="doFoo" access="remote"...>
    <cfargument name="dsn" type="String" required="false" default="#variables.datasource#" />
</cffunction>

我将使用您应用的会话范围来存储用户dsn名称,并使用该var传递给AJAX调用。

答案 1 :(得分:2)

您应该创建一个“init”方法,该方法将用作CFC的构造函数。然后,您可以实例化CFC并将它们存储在共享范围中,很可能是应用程序范围。从这里开始,通过AJAX使用这个CFC,我通常会创建一个远程外观。基本上这是另一个将直接访问应用程序范围中的CFC实例的CFC。它将实现您需要通过Ajax访问的方法,使用access="remote"公开它们,使您的应用程序可以访问实际CFC中的access="public"方法。在这种情况下,通常接受远程外观可以直接访问应用程序范围作为设计模式的一部分。

一个简单的例子:

example.cfc:

<cfcomponent output="false">
    <cffunction name="init" access="public" output="false" returntype="any">
        <cfargument name="dsn" type="string" required="true" />
        <cfset variables.dsn = arguments.dsn />
        <cfreturn this />
    </cffunction>
    <cffunction name="doStuff" access="public" output="false" returntype="query">
        <cfset var q = "" />
        <cfquery name="q" datasource="#variables.dsn#">
        select stuff from tblStuff
        </cfquery>
        <cfreturn q />
    </cffunction>
</cfcomponent>

在Application.cfc onApplicationStart()方法中:

<cfset application.example = createObject("component","example").init(dsn = "somedsn") />

remote.cfc:

<cfcomponent output="false">
    <cffunction name="doStuff" access="remote" returntype="query">
        <cfreturn application.example.doStuff() />
    </cffunction>
</cfcomponent>

答案 2 :(得分:1)

您可以在Application.cfc中的onRequest或onRequestStart函数中设置数据源变量

<cffunction name="onSessionStart">
    <cfset session.dsn = _users_personal_dsn_ />
</cffunction>


<cffunction name="onRequestStart" >
   <cfset dsn = "#session.dsn#" />
</cffunction>

<cfquery name="qEmployees" datasource="#dsn#">
    SELECT *
    FROM #SESSION.DatabaseSchema#.Employees
</cfquery>

等。

不确定这是否有效[未经测试 - 实际感觉有点草率] -Sean

答案 3 :(得分:1)

您选择的范围(对于此问题的任何变体,而不仅仅是DSN)应该基于值的生命周期是否与范围的生命周期相同。

在我们的应用程序中,DSN只在应用程序的生命周期中设置一次,因此我们在onApplicationStart中创建了一个application.config结构(从文件中解析),其中包含application.config.dsn < / p>

如果您的值确实在会话之间发生变化,但在会话期间没有变化,请继续使用会话范围。

如果您的值可以针对任何给定请求更改,但不能在请求中间更改,请将其放在请求范围内。

那就是说,仍然听从ryan的建议,并添加仅默认为此值的可选参数:灵活永远是最好的。

答案 4 :(得分:0)

我的建议是创建一个基类,然后让需要数据库访问的组件扩展该组件。它不必在直接的父层次结构中,而是在某个地方。

他们的目标是做两件事,保持cfc从主程序中抽象出来并保持其易于配置。这完成了两者。

所以查询数据库的CFC看起来像这样:

<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
    <cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>

上面的关键是extends="DataAccessBase"部分。这增加了抽象层,您可以在一个可配置点控制数据访问,但它不依赖于应用程序本身,而是将组件从实现的位置抽象出来。

您的DataAccessBase.cfc看起来像这样:

<cfcomponent>
<cffunction name="loadSettings">
    <cfparam name="request.settings" default="#structNew()#">
    <cfparam name="request.settigns.loaded" default="false">
    <cfif request.settings.loaded eq false>
        <!--- load settings from resource bundle etc --->
        <cfset request.settings.dsn     = 'myDSN'>
        <cfset request.settings.loaded  = true>
    </cfif>
</cffunction>
<cffunction name="getDsn" access="public" returntype="string">
    <cfset loadSettings()>
    <cfreturn request.settings.dsn>
</cffunction>

您当然可以更加复杂地配置和存储设置等,但这超出了我认为的问题范围。 :)

我认为没有理由在每次方法调用时传递DSN。是的,它有效,但没有必要。这些组件是使用数据结构的内置假设开发的,因此您知道它不会从addItem()调用更改为updateItem()调用,因此它的重复工作意味着额外的故障点。 :P

有意义吗?