Использование области запроса для хранения объекта - PullRequest
2 голосов
/ 02 февраля 2011

Я использую CFWheels для разработки приложения в ColdFusion.

У меня есть модель под названием Vote.cfc. Прежде чем объект голосования может быть создан, обновлен или удален, мне нужно получить объект публикации из другой модели: Post.cfc. Голосование принадлежит посту. В сообщении много голосов.

Используя данные из объекта post, мне нужен объект to validate the vote по нескольким критериям и нескольким функциям. Единственный способ сохранить объект post, чтобы он был доступен для этих функций, - это сохранить его в области запроса.

Другие говорили, что это плохая практика. Но я не смог выяснить почему. Я думал, что объем запроса является потокобезопасным, и было бы разумно использовать его в этой ситуации.

Моя другая альтернатива - загружать новый экземпляр объекта post в каждую функцию, которая в этом нуждается. Хотя Wheels использует кеширование, это привело к увеличению времени запроса на 250%.

UPDATE

Вот несколько примеров. Во-первых, контроллер обрабатывает, чтобы увидеть, существует ли уже объект голосования. Если это так, он удаляет его, если нет, то создает его. Функция контроллера по сути является функцией переключения.

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

После этого обратный вызов, зарегистрированный в модели, срабатывает перед проверкой. Он вызывает функцию, которая извлекает объект сообщения, которому будет принадлежать голос. Функция getPost () сохраняет сообщение в области запроса. Теперь он доступен для ряда функций проверки в модели.

// 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 ]

2 голосов
/ 02 февраля 2011

Обновление с новым ответом, основанным на цепочке комментариев в OP

Поскольку вы расширяете базовую форму объекта голосования VoteLike.cfc, CFC совместно используют локальную область потоковых безопасных переменных (если выне хранить это в области приложения или сервера, где это может быть изменено другими).Это означает, что вы устанавливаете значение, такое как Variables.Post, один раз и ссылаетесь на него в любой функции в стеке CFC.

Поэтому измените вашу функцию getPost () на:

beforeValidation("getPost");

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

Теперь внутри любой функции в любом из VoteLike.cfc вы можете ссылаться на Variables.Post.Это означает, что у вас есть один экземпляр Post в локальной области переменных CFC, и вам не нужно передавать его через аргументы или другие области действия.

Оригинальный ответ ниже

The "самый правильный "способ справиться с этим" - передать объект каждой отдельной функции в качестве аргумента.Или добавьте сообщение в качестве свойства объекта голосования, чтобы объект голосования имел доступ к полному объекту сообщения.

<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, аВторой имеет имя как Даниил.Это потому, что CFC передаются в функции по ссылке, что означает, что любые изменения, сделанные в этой функции, вносятся в исходный объект в памяти.Следовательно, нет воссоздания объектов, поэтому производительность не падает.

Дан

1 голос
/ 02 февраля 2011

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>

Недостатком является то, что если во время запроса что-то еще изменится, у вас будет устаревший кеш, но, похоже, вы не ожидаете никаких изменений во время выполнения.

...