Coldfusion 当CFC用于数据源和数据库模式时,如何避免在CFC中使用会话变量?

Coldfusion 当CFC用于数据源和数据库模式时,如何避免在CFC中使用会话变量?,coldfusion,scope,session-variables,cfc,application-variables,Coldfusion,Scope,Session Variables,Cfc,Application Variables,我正试图重构我所有的CFC,以避免使用会话和应用程序变量,这不是一项容易的任务 但是,在此应用程序中,会话变量用于每个数据库调用,因为不同的登录用户可能正在访问不同的数据库和模式: <cfquery name="qEmployees" datasource="#SESSION.DataSourceName#"> SELECT * FROM #SESSION.DatabaseSchema#.Employees </cfquery> 我不想麻烦地将这两个会话

我正试图重构我所有的CFC,以避免使用会话和应用程序变量,这不是一项容易的任务

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

<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
    SELECT *
    FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
我不想麻烦地将这两个会话变量传递给每个访问数据库的方法调用。这种情况尤其如此,因为我不想在远程AJAX调用中传递DSN和模式名称


对于cfc中不应使用的所有作用域,执行此操作的最佳实践是什么?

能否在应用程序的onRequest或onRequestStart函数中设置数据源变量。cfc

<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

能否在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

我认为,由于数据源确实是变量,我会将其作为可选参数传递到每个函数中,并将默认值设置为变量范围的dsn属性。我在CFC的构造函数中设置了DSN范围内的变量。这样,您只需为AJAX调用传入DSN

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

我会使用应用程序的会话范围来存储用户dsn名称,并使用该变量来传递到AJAX调用。

我认为,由于数据源确实是变量,所以我会将其作为可选参数传递到每个函数中,并将默认值设为变量范围的dsn属性。我在CFC的构造函数中设置了DSN范围内的变量。这样,您只需为AJAX调用传入DSN

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

我会使用应用程序的会话作用域来存储用户dsn名称,并使用该var传递到AJAX调用。

对于这个问题的任何变体,而不仅仅是dsn,您选择的作用域应该基于值的生存期是否与作用域的生存期相同

在我们的应用程序中,DSN在应用程序的生命周期中只设置一次,因此我们有一个application.config结构,它是从onApplicationStart中的文件解析创建的,其中包含application.config.DSN

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

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


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

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

在我们的应用程序中,DSN在应用程序的生命周期中只设置一次,因此我们有一个application.config结构,它是从onApplicationStart中的文件解析创建的,其中包含application.config.DSN

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

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


也就是说,仍然听从ryan的建议,并添加仅默认为该值的可选参数:灵活始终是最好的。

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

他们的目标是做两件事,将cfc从主程序中抽象出来,并使其易于配置。这两者都可以实现

因此,查询数据库的CFC如下所示:

<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
    <cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>
当然,您可以更加复杂地配置和存储设置等,但我认为这超出了问题的范围

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


有意义吗?

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

他们的目标是做两件事,将cfc从主程序中抽象出来,并使其易于配置。这两者都可以实现

因此,查询数据库的CFC如下所示:

<cfcomponent extends="DataAccessBase">
<cffunction name="myFunction" access="public" returntype="string">
    <cfquery datasource="#getDSN()#" name="qStuff">select * from table</cfquery>
</cffunction>
当然,你可以用h变得更复杂 如何配置和存储设置等,但我认为这超出了问题的范围:

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


有意义吗?

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

一个简单的例子:

例如: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>

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

一个简单的例子:

例如: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>


如果它不仅仅是一个dsn,您可以查看facades**在我看来,答案将取决于您的CFC的创建位置:如果在会话范围内,则尝试Sean Coyne的方法,在onSessionStart中传递会话dsn/模式。如果它们在应用程序范围内,那么苦乐参半是正确的,dsn/模式将根据会话用户的不同而变化,因此为了安全起见,您必须通过远程facade为每个调用传递这些值。如果不仅仅是dsn,您可以查看facades**在我看来,答案将取决于创建CFC的位置:如果在会话范围内,则尝试Sean Coyne的方法,在onSessionStart中传递会话dsn/schema。如果它们在应用程序范围内,那么苦乐参半是正确的,dsn/模式将根据会话用户的不同而变化,因此为了安全起见,每次调用都必须通过远程facade传递这些值。数据源和模式值仅在会话之间变化,而不是在一个会话内。这似乎太过分了。埃里克,你能澄清一下吗?当一个会话结束时,下一个会话的数据源和模式可能会改变吗?这是用于负载平衡类型的情况吗?架构和数据库基于登录。每次登录都会创建一个新会话。这允许一个网站允许不同类型的用户。数据源和模式值仅在会话之间可变,而不是在一个会话内。这似乎太过分了。埃里克,你能澄清一下吗?当一个会话结束时,下一个会话的数据源和模式可能会改变吗?这是用于负载平衡类型的情况吗?架构和数据库基于登录。每次登录都会创建一个新会话。这允许一个网站允许不同类型的用户。您的意思是复制请求范围中的会话变量吗?因此,如果SESSION.DataSourceName设置为customersX,那么在onRequestStart中,我将REQUEST.DataSourceName设置为SESSION.DataSourceName,并在所有CFQUERY标记中使用REQUEST.DataSourceName?实际上,我喜欢这个想法-这与我使用UDFs Ben Nadel的模型类似,即将UDF存储在应用程序范围中,然后将其复制到onRequestStart中的请求范围。如果有人发现此实现有问题,请告诉我,因为这是我学习的方式。我认为这比使用应用程序或会话范围的变量更糟糕,因为现在dsn根本没有范围,这意味着它可能更容易被意外覆盖!当然,您希望在CFC中避免应用程序或会话的原因是为了将它们封装起来:即所有变量都显式地传入和传出,而不是取决于外部作用域应用程序、会话、请求、可用变量。您的意思是复制请求作用域中的会话变量吗?因此,如果SESSION.DataSourceName设置为customersX,那么在onRequestStart中,我将REQUEST.DataSourceName设置为SESSION.DataSourceName,并且只在所有会话中使用REQUEST.DataSourceName

我的CFQUERY标签?实际上,我喜欢这个想法-这与我使用UDFs Ben Nadel的模型类似,即将UDF存储在应用程序范围中,然后将其复制到onRequestStart中的请求范围。如果有人发现此实现有问题,请告诉我,因为这是我学习的方式。我认为这比使用应用程序或会话范围的变量更糟糕,因为现在dsn根本没有范围,这意味着它可能更容易被意外覆盖!当然,您希望在CFC中避免应用程序或会话的原因是为了将它们封装起来:即所有变量都显式地传入和传出,而不是依赖于外部作用域app、session、request、,变量可用。数据源和架构值是会话之间的变量-不在一个会话内。数据源和架构值是会话之间的变量-不在一个会话内。如果我误解了,我很抱歉,但是,这不正是我试图做的吗?我试图避免访问CFCs中的应用程序和会话范围。正如我所指出的:在这种情况下,人们普遍认为远程facade可以作为设计模式的一部分直接访问应用程序范围。在本例中,您将创建对象实例的远程外观。它将从您的实际对象中删除对共享范围的依赖,这正是您尝试执行的操作。不再需要直接从对象访问会话范围。只有远程facade依赖于共享作用域,此外,它只需要访问共享作用域就可以与您创建的对象的实例进行对话。雷·卡姆登(Ray Camden)最近写了一篇博客文章,他在一个例子中使用了同样的方法。您可以在这里看到:此外,您还可以使用ColdSpring为您管理和创建远程facade。这是一种很好的方法,但不能解决DSN/模式因每个会话而异的需要。不过,如果您将CFC存储在会话范围而不是应用程序范围中,则可以。所以application.example应该是session.example,在onSessionStart中创建,而不是在ApplicationStart中创建。然后,在init中传递的dsn/schema值可以是特定于会话的。如果你不想在每次会议上都要你的CFC副本,那么你需要遵循苦乐参半的建议,将会议变量传递到你的应用范围CFC的每次呼叫中。如果我误解了,我很抱歉,但是,这不正是我试图做的吗?我试图避免访问CFCs中的应用程序和会话范围。正如我所指出的:在这种情况下,人们普遍认为远程facade可以作为设计模式的一部分直接访问应用程序范围。在本例中,您将创建对象实例的远程外观。它将从您的实际对象中删除对共享范围的依赖,这正是您尝试执行的操作。不再需要直接从对象访问会话范围。只有远程facade依赖于共享作用域,此外,它只需要访问共享作用域就可以与您创建的对象的实例进行对话。雷·卡姆登(Ray Camden)最近写了一篇博客文章,他在一个例子中使用了同样的方法。您可以在这里看到:此外,您还可以使用ColdSpring为您管理和创建远程facade。这是一种很好的方法,但不能解决DSN/模式因每个会话而异的需要。不过,如果您将CFC存储在会话范围而不是应用程序范围中,则可以。所以application.example应该是session.example,在onSessionStart中创建,而不是在ApplicationStart中创建。然后,在init中传递的dsn/schema值可以是特定于会话的。如果您不想在每次会话中都要CFC的副本,那么您需要遵循苦乐参半的建议,将会话变量传递到您的应用范围CFC的每次调用中。