使用请求范围来存储对象

时间:2011-02-02 18:44:39

标签: coldfusion scope request cfwheels

我正在使用CFWheels在ColdFusion中开发应用程序。

我有一个名为Vote.cfc的模型。在创建,更新或删除投票对象之前,我需要从另一个模型中获取帖子对象:Post.cfc。投票属于邮政。一个帖子有很多票。

使用来自post对象的数据,我需要跨多个条件和多个函数的to validate the vote对象。我能想到持久化post对象以便它们可用于这些函数的唯一方法是将它存储在请求范围内。

其他人说这是不好的做法。但我无法找出原因。我认为请求范围是线程安全的,在这种情况下使用它是有意义的。

我的另一种选择是在每个需要它的函数中加载post对象的新实例。虽然Wheels使用缓存,但这样做会导致请求时间增加250%。

更新

这是一些样本。首先,控制器处理以查看投票对象是否已存在。如果是,则删除它,如果没有,则创建它。控制器功能本质上是一个切换功能。

Votes.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,这是我认为减慢请求的速度!

2 个答案:

答案 0 :(得分:2)

根据OP

中的评论主题更新新答案

由于您正在扩展VoteLote.cfc的基本投票对象,因此CFC共享一个本地线程安全变量范围(只要您不将其存储在应用程序或服务器范围内,而其他人可能会将其存储在其中)。这意味着您设置一个值,例如Variables.Post,并在CFC堆栈中的任何函数中引用它。

所以将你的getPost()函数更改为:

beforeValidation("getPost");

private function getPost(){
    Variables.Post = model("post").findByKey(this.postID);
}

现在,在VoteLike.cfc的任何函数中,您都可以引用Variables.Post。这意味着您在本地CFCs变量范围中有一个Post实例,并且您不必通过参数或其他范围传递它。

以下原始答案

处理此问题的“最正确”方法是将对象作为参数传递给每个单独的函数。那,或者将Post添加为Vote对象的属性,以便Vote对象可以访问完整的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的副本,这也应该考虑到您的性能问题。

代码示例更新

为了说明上面的“按引用”评论,请设置以下文件并将它们放在一个目录中。

<强> TestObject.cfc

<cfcomponent output="false">
    <cfproperty name="FirstName" displayname="First Name" type="string" />
    <cfproperty name="LastName" displayname="Last Name" type="string" />

</cfcomponent>

<强> index.cfm

<!--- 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时,你会注意到第一个转储的FirstName为Dan,而第二个转储的FirstName为Daniel。这是因为CFC通过引用传递给函数,这意味着在该函数中进行的任何更改都是对内存中的原始对象进行的。因此没有物体的重建,所以没有性能受到打击。

答案 1 :(得分:1)

request是一个全局范围,仅限于页面请求(即线程安全),它在该页面请求期间存在。所以这是不好的做法,就像全局变量是一种不好的做法一样,但持续时间很短。我认为这是在你所描述的情况下将数据抛到空中或围栏上,其中任何其他代码都可以从半空中拔出。

因此,对于您的情况,它可能很好 - 在消费点添加一些有用的参考,或许首先将数据放入request范围的位置。这就是说,如果你每次都回到相同的源方法,考虑在负责创建该对象的任何函数内部缓存(例如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>

下行是指在请求期间其他内容会更改数据,您将拥有过时的缓存,但看起来您不希望在执行期间发生任何更改。