Using CB 2.6.3, the autowire interceptor, and the "cfproperty" way to
inject dependencies.
1. Code
In my code, I am injecting a form object into my handler and a generic
error object is injected into the form object. Adhoc representation
below
Handler [ Form Object { Generic Error Object } ]
2. Question
How are others dealing with situations where you want to inject an
object but, ideally, you'd need a new copy of that object for every
request?
Specific to this example, the generic error object should not be
shared between requests because I don't want the error messages to
overlap between users. So each request would ideally have its own copy
of the generic error object that exists for the duration of the
request.
I have found some working solutions but I'm just wondering if others
have come up with better ways to accomplish this.
3. Possible Solutions
- a. Works: If I inject the generic error object using the "session"
scope in the cfproperty tag, then I have a working solution because
each session gets its own error object. The messages persist over
requests but they are contained to the same user (session) and I am
able to "reset" the object as needed so that it does not have
messages.
Downside: Works but it seems like overkill to store the error object
in the session scope when I just need the object for the duration of
the request.
- b. Works: If I don't cache the handler, the everything gets
recreated and this is not an issue.
Downside: Works but then I lose all the benefits of caching. Each
request doesn't need a fresh copy of the handler or form objects so
ideally they should be cached.
- c. No Work: If I use "Request" as the scope in the cfproperty tags,
it errors out.
I try to use my own transient factory in similar situation since I use cs to manage singletons and cb to autowire them, however with transients (or fresh objects) I use a factory to inject what I need into the targets I need to…
Thank you Adam. Is your transient factory something that falls primarily into the coldspring side of things in your code? I’m new to a lot of this so I’ve tried to avoid coldspring until there was a need. While I do have a coldbox only working solution, I’m not that comfortable with it so this may be the wall I was waiting for to look into coldspring more deeply.
If anyone handles this using only coldbox, it’s be great to know the approach you take.
I use the ColdBox dependency injection and autowiring to inject
Services into my handlers and then whatever needs to be wired into my
services (dao, gateway, beans, etc). I would probably create an
ErrorService and but my ErrorFactory into my ErrorService to create
blank error beans.
I tend to do injection into "instance" so in my handler I would call
instance.ErrorService.createError() and then in my ErrorService I
would have a createError method that calls the ErrorFactory that I
inject there to handle the creation/population/destruction of the
transient beans.
Caveat: I'm pretty new at this still, so take it with a grain of salt.
Thank you Judah. You’ve given me an avenue to explore if I want to keep it all in coldbox.
What I was trying to accomplish was along the lines of: “Coldbox, you already know what this object is because it is in your cache. Just give me a clone of it for every request”.
Your suggestion says to me: “Coldbox, call the error service and create a generic error object on demand”
Yes, basically. I think that your error objects are transients. They
should persist for the life of a page request since a request for
validation comes in, error object is generated, then if it passes
validation, error object goes away, if it does not pass validation,
then display is invoked and the error object is used, then go away.
The error object shouldn't need to live beyond that.
So essentially you want a factory that says "I have a template for
creating these objects and I know how to create them and populate
them" and then you call the factory in your validation and say "hey, I
need an object" and then optionally, "hey, please stuff this stuff in
the object so I can display it later".
the coldbox model integration works as a transient factory also. So if
you need a transient model object just don't put any cache metadata on
it. Then they will be on a per USe basis. No need for custom
transient factories. The model integration can also do it
I’m creating a barebones demo to do further testing but right now the transient feature only works for me if I call the model using “getModel()” in my handler. If I inject it into my handler using a cfproperty tag, the object is persisted across all requests. In both cases, the cache metadata is set to false.
Working on replicating my results in a barebones “application template” app.
Well,if you are injecting into the handler, that denotes persistence. You would have to use getModel() in order to not make it part of the handler lifecycle, there is no way around that, unles you are building proxy objects, and well, that adds another layer of complexity.
if you make calls to getModel(), you are simulating the transient factory if the objects do not have caching on them, if they do, the getModel() calls will either create or return from cache.
Luis, in your example, what is the bridge between the handler and the model? A service object like in Judah’s responses?
In other words, if I don’t want to use getModel() but still want to get to my model object from my handler and have the model object be transient, what’s the middle man that connects the handler and the model. A service object?
Thanks for that point Luis, I didn't realize that getModel() would do
that with transients. I think that the question that Dorioo had on
that, which I have as well now, is whether calling your model directly
from the handler is best practices.
Thus far I've leaned toward putting all my access to the model in a
service layer and then injecting the service layer (as a singleton)
into my handlers. That way if I need to change the model I know where
I expose the model to the controller. On the other hand, if I change
the model I'm probably going to have to change the service layer and
then I'll need to check my handlers to see if I need to change how
they are interacting with the service layer which I guess is
fundamentally equivalent to looking for places in the handler where it
interacts with the model directly through getModel()
What are people's thoughts on the two paradigms in terms of best practices?
Just wanted to record my current solution for this thread’s posterity. My original question was about solutions for creating what I called a “fresh object” and learned from the responses is more appropriately called a “transient object”. In hindsight, my original solutions were not really solutions at all.
My current solution is to have a very simple object that uses the “getModel()” function. So if I need a transient object, I make sure the cache metadata is not set in the model and then I call the simple object which calls the “getModel()” function for whatever model I pass to it. So, regardless of what is cached, the getModel() call “simulates the transient factory” as Luis mentioned and I get my fresh aka transient object.