Как можно избежать использования переменных SESSION в CFC, когда они используются для схем источника данных и базы данных?
Я пытаюсь реорганизовать все свои CFC, чтобы избежать использования переменных SESSION и APPLICATION (задача не из легких).
Однако в этом приложении переменные SESSION используются при каждом вызове базы данных, поскольку разные вошедшие в систему пользователи могут обращаться к разным базам данных и схемам:
<cfquery name="qEmployees" datasource="#SESSION.DataSourceName#">
SELECT *
FROM #SESSION.DatabaseSchema#.Employees
</cfquery>
Я не хочу проходить через проблему передачи этих двух переменных SESSION в каждый вызов метода, который обращается к базе данных. Это особенно актуально, так как я не хочу передавать DSN и имена схем в удаленных вызовах AJAX.
Какова наилучшая практика для этого - для всех областей, которые не должны использоваться в ХФУ?
5 ответов
Вы должны создать метод "init", который будет служить конструктором для вашего CFC. Затем можно создать экземпляры CFC и сохранить их в общей области, скорее всего, в области приложения. Отсюда, чтобы использовать этот CFC через AJAX, я обычно создаю удаленный фасад. По сути, это еще один CFC, который будет напрямую обращаться к экземпляру CFC в области приложения. Он будет реализовывать методы, которые вам нужны для доступа через Ajax, выставлять их используя access="remote"
предоставление вашему приложению доступа к 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>
Я думаю, что поскольку источник данных действительно является переменным, я передал бы его каждой функции в качестве необязательного параметра и установил бы значение по умолчанию для атрибута dsn переменной области. Я бы установил переменную DSN в конструкторе CFC. Таким образом, вам нужно только передать DSN для вызовов AJAX.
<cffunction name="doFoo" access="remote"...>
<cfargument name="dsn" type="String" required="false" default="#variables.datasource#" />
</cffunction>
Я бы использовал сессионную область вашего приложения для хранения имени пользователя dsn и использовал бы эту переменную для передачи вызова AJAX.
Можете ли вы установить переменные источника данных в функциях onRequest или onRequestStart в вашем Application.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
Выбранная область действия (для любого варианта этого вопроса, а не только для уведомлений о доставке) должна основываться на том, совпадает ли время жизни значения с продолжительностью существования области.
В нашем приложении DSN устанавливается только один раз за время существования приложения, поэтому у нас есть структура application.config, которая создается (анализируется из файла) в onApplicationStart, и внутри нее находится application.config.dsn
Если ваша ценность действительно меняется между сессиями, но не в течение жизни сеанса, продолжайте и используйте область сеанса.
Если ваше значение может измениться для какого-либо данного запроса, но не в середине запроса, поместите его в область запроса.
Тем не менее, все же прислушайтесь к совету Райана и добавьте необязательные аргументы, которые по умолчанию используются только в этом значении: гибкость - всегда лучшее.
Для этого я предлагаю создать базовый класс, а затем сделать так, чтобы ваши компоненты, которым требуется доступ к базе данных, расширяли этот компонент. Это не должно быть в непосредственной родительской иерархии, но где-то вниз по линии.
Их цель состоит в том, чтобы сделать две вещи, сохранить абстракцию 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(), таким образом, его дублирование работы, что означает дополнительные точки отказа.:П
Есть смысл?