Coldfusion 使用请求范围存储对象
我正在用ColdFusion开发一个应用程序 我有一个模型叫Coldfusion 使用请求范围存储对象,coldfusion,scope,request,cfwheels,Coldfusion,Scope,Request,Cfwheels,我正在用ColdFusion开发一个应用程序 我有一个模型叫Vote.cfc。在创建、更新或删除投票对象之前,我需要从另一个模型获取post对象:post.cfc。投票属于一个职位。一个职位有很多选票 使用来自post对象的数据,我需要跨多个条件和多个函数验证vote对象。我能想到的持久化post对象的唯一方法是将其存储在请求范围中,以便这些函数可以使用它 其他人说这是一种不好的做法。但我无法找到原因。我认为请求范围是线程安全的,在这种情况下使用它是有意义的 我的另一种选择是在每个需要post对
Vote.cfc
。在创建、更新或删除投票对象之前,我需要从另一个模型获取post对象:post.cfc
。投票属于一个职位。一个职位有很多选票
使用来自post
对象的数据,我需要跨多个条件和多个函数验证vote
对象。我能想到的持久化post对象的唯一方法是将其存储在请求范围中,以便这些函数可以使用它
其他人说这是一种不好的做法。但我无法找到原因。我认为请求范围是线程安全的,在这种情况下使用它是有意义的
我的另一种选择是在每个需要post对象的函数中加载它的新实例。尽管Wheels使用缓存,但这样做会导致请求时间增加250%
更新
这是一些样品。首先,控制器处理查看投票对象是否已经存在。如果有,则删除它;如果没有,则创建它。控制器功能本质上是一个切换功能
vows.cfc控制器
private void function toggleVote(required numeric postId, required numeric userId)
{
// First, we look for any existing vote
like = model("voteLike").findOneByPostIdAndUserId(values="#arguments.postId#,#arguments.userId#");
// If no vote exists we create one
if (! IsObject(like))
{
like = model("voteLike").new(userId=arguments.userId, postId=arguments.postId);
}
else
{
like.delete()
}
}
VoteLike.cfc型
之后,在验证之前,在模型中注册的回调将触发。它调用一个函数来检索投票将属于的post对象。函数getPost()将post存储在请求范围中。它现在可用于模型中的一组验证函数
// Load post before Validation via callback in the Constructor
beforeValidation("getPost");
private function getPost()
{
// this.postId will reference the post that the vote belongs to
request.post = model("post").findByKey(this.postId);
}
// Validation function example
private void function validatesIsNotOwnVote()
{
if (this.userId == request.post.userId)
{
// addError(message="You can't like your own post.");
}
}
getPost()函数的替代方法是使用作用域调用“this.post().userId
”来获取post对象,如下所示:
private void function validatesIsNotOwnVote()
{
if (this.userId == this.post().userId)
{
addError(message="can't vote on your own post")
}
}
但是我必须为每个函数重复这个作用域调用this.post().userId
,我认为这会减慢请求的速度 根据OP中的注释线程更新新答案
由于您正在扩展基本投票对象表单VoteLike.cfc,因此cfc共享一个本地线程安全变量范围(只要您不将其存储在应用程序或服务器范围内,其他人可以对其进行修改)。这意味着您可以在CFC堆栈中的任何函数中设置一个值,例如Variables.Post、once和reference
因此,将getPost()函数更改为:
beforeValidation("getPost");
private function getPost(){
Variables.Post = model("post").findByKey(this.postID);
}
现在,在VoteLike.cfc中的任何函数中,都可以引用Variables.Post。这意味着您在本地CFCs变量范围内有一个Post实例,您不必通过参数或其他范围传递它
原始答案如下
处理此问题的“最正确”方法是将对象作为参数传递给每个单独的函数。或者将Post添加为投票对象的属性,以便投票对象可以访问完整的Post对象
<cffunction name="doTheValidation">
<cfargument name="ThePost" type="Post" required="true" />
<cfargument name="TheVote" type="Vote" required="true" />
<!--- do stuff here --->
</cffunction>
这是一种不好的做法,因为您要求您的对象可以访问外部作用域,该作用域可能存在,也可能不存在,以便执行其工作。如果您决定要在一个页面上处理多个投票或帖子,那么您必须摆弄外部范围以使事情正常运行
你最好把你的物体传来传去,或者用构图把你的物体拼凑在一起,让它们彼此都知道
更新
关于性能问题,如果使用组合将对象绑定在一起,那么内存中只有两个对象,因此不必实例化一堆不必要的Post对象。此外,将CFC作为参数传递给函数将通过引用传递CFC,这意味着它不会在内存中创建CFC的副本,这也应该考虑性能问题
代码示例更新
为了说明上面的“by reference”注释,请设置以下文件并将它们单独放在一个目录中
TestObject.cfc:
<cfcomponent output="false">
<cfproperty name="FirstName" displayname="First Name" type="string" />
<cfproperty name="LastName" displayname="Last Name" type="string" />
</cfcomponent>
<!--- Get an instance of the Object --->
<cfset MyObject = CreateObject("component", "TestObject") />
<!--- Set the initial object properties --->
<cfset MyObject.FirstName = "Dan" />
<cfset MyObject.LastName = "Short" />
<!--- Dump out the object properties before we run the function --->
<cfdump var="#MyObject#" label="Object before passing to function" />
<!--- Run a function, sending the object in as an argument, and change the object properties --->
<cfset ChangeName(MyObject) />
<!--- Dump out the object properites again, after we ran the function --->
<cfdump var="#MyObject#" label="Object after passing to function" />
<!--- Take a TestObject, and set the first name to Daniel --->
<cffunction name="ChangeName">
<cfargument name="TheObject" type="TestObject" required="true" hint="" />
<cfset Arguments.TheObject.FirstName = "Daniel" />
</cffunction>
index.cfm:
<cfcomponent output="false">
<cfproperty name="FirstName" displayname="First Name" type="string" />
<cfproperty name="LastName" displayname="Last Name" type="string" />
</cfcomponent>
<!--- Get an instance of the Object --->
<cfset MyObject = CreateObject("component", "TestObject") />
<!--- Set the initial object properties --->
<cfset MyObject.FirstName = "Dan" />
<cfset MyObject.LastName = "Short" />
<!--- Dump out the object properties before we run the function --->
<cfdump var="#MyObject#" label="Object before passing to function" />
<!--- Run a function, sending the object in as an argument, and change the object properties --->
<cfset ChangeName(MyObject) />
<!--- Dump out the object properites again, after we ran the function --->
<cfdump var="#MyObject#" label="Object after passing to function" />
<!--- Take a TestObject, and set the first name to Daniel --->
<cffunction name="ChangeName">
<cfargument name="TheObject" type="TestObject" required="true" hint="" />
<cfset Arguments.TheObject.FirstName = "Daniel" />
</cffunction>
运行index.cfm时,您会注意到第一个转储的名字是Dan,而第二个转储的名字是Daniel。这是因为CFC是通过引用传递给函数的,这意味着在该函数中所做的任何更改都会对内存中的原始对象进行。因此,没有对象的再创造,因此没有性能冲击
Dan是一个全局作用域,仅限于页面请求(即线程安全),在该页面请求期间有效。因此,这是一种不好的做法,就像全局变量是一种不好的做法一样,但持续时间很短。我认为这是在你描述的情况下,将数据抛向空中或越过栅栏,任何其他代码都可以从空中拔出 因此,对于您的情况来说,这可能很好-在消费点添加一些有用的参考,可能是关于数据首先放置在
请求范围中的位置。也就是说,如果您每次返回同一个源方法,考虑缓存在任何函数中负责创建其中的对象(例如<代码> GETVoTE())/>代码>,并且您可以使用这样的请求范围:
<cfparam name="request.voteCache" default="#structNew()#"/>
<cffunction name="getVote" output="false" access="public" returntype="any">
<cfargument name="voteId" type="string" required="true"/>
<cfif structKeyExists(request.voteCache, arguments.voteId)>
<cfreturn request.voteCache[arguments.voteId]>
</cfif>
<!--- otherwise get the vote object --->
<cfset request.voteCache[arguments.voteId] = vote>
<cfreturn vote>
</cffunction>
不利的一面是,如果在请求过程中有其他内容更改了数据,那么您将有一个过时的缓存,但在执行过程中您似乎不希望有任何更改 这是如何解决问题中提出的性能问题的?因为您没有创建对象的其他实例,正如原始海报所述,这是他不使用请求范围的解决方法。你要么使用组合,仍然只处理两个对象,要么将对象传递到函数中,这仍然没有实例化一个新对象。