if I put a custom class in session, then in an action method I get an instance of that class from session, and populate some fields, I noticed that when a different controller gets that class from session, those fields are populated. Even though after the first call didn't save the updated class back in session.
is this typical behavior for session objects?
I thought I had to use keyword 'static' on the class in session for this to happen
thanks
As your class is a reference type this is the normal behavior. Here's what's happening in memory:
You instantiate an object and put it in session
var someObj = new SomeObject();
Session["someObj"] = someObj;
At this stage a new object is created on the heap and Session["someObj"] is pointing to this object.
You retrieve the object from session in controller A and modify some property but you do not call Save:
var someObj = (SomeObject)Session["someObj"];
someObj.SomeProp = "new value";
Now someObj is pointing to this same object you created earlier on the heap. As someObj is only a reference you are actually modifying the original object in memory.
You retrieve the object from session in Controller B:
var someObj = (SomeObject)Session["someObj"];
Now someObj points to the same memory location which has been modified.
is this typical behavior for session objects?
Well yes, but, it is typical of .NET objects in general. You took a reference, so you were changing the original object still pointed to by the session.
Since your session is in memory this is the expected behavior. If you store the session in an external store then you have to save the objects back to the session to get that same behvior. I would try to avoid such development becuase when you do change the store the behavior is totally different.
Related
In our Rails application we want to store an instance of a class in a session. This is because we want to set the class with some parameters when the user first logs into the application and then re-use this class by pulling it back out of the same session. When their session expires or they log out, this instance of the class is destroyed.
We're doing this to avoid using a Singleton class because that would live at Application-level and be available on different processes and stick around longer than the user's session, and have security implications due to it also being available to other users who haven't created a session yet.
So this is how it works:
session[:example_class] = ExampleClass.new(field_one: 'field_one', field_two: 'field_two')
This works fine!
However if I then do this:
current_instance = session[:example_class]
current_instance.do_something
session[:example_class] = current_instance
Whereby I am calling a method on this instance or whatever and then want to push that updated instance back into the session again so it's stored somewhere... we get this error:
TypeError in HomeController#index
ExampleClass can't be referred to from /Users/cameron/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/bundler/gems/activerecord-session_store-376ed7f7aba1/lib/active_record/session_store.rb:64:in `dump'
And that method that is failing in session_store.rb is:
def self.dump(value)
::Base64.encode64(Marshal.dump(value))
end
And the value it's trying to store is:
{"_csrf_token"=>"nrw4m2ZAECwD3TiaPZoaSt4vL1DvjO+COnBpUQGwpXs=", "example_class"=>#<ExampleClass:0x00007f7fa7b1b998 #field_one="field_one", #field_two="field_two">}
Why can I write the class in the first time around... but afterwards it throws that error?
And how we get around this?
I can't answer you fully why it fails, but I wouln't rely on implicit serialization of Ruby objects.
I would find a way to serialize the object of ExampleClass explicitly (similar to as_json) - convert to a hash, then store the hash in the session. When you need this object again, initialize ExampleClass instance with the params from session and then serialize it back to session.
I don't have an answer for why the above error happened but it turns out if you're storing an instance of a class inside the session, you're in fact storing the instance and not just the current state of the class as I originally thought it was doing.
This basically means that whenever you interact with that class and change it's attributes the session is still reading from the same instance you are interacting with and therefore has the same information without having to write back into the session.
In short you have the behaviour of a Singleton class but using a session to maintain state.
When I am testing for a particular project I create a new instance of an ActiveRecord object using
ObjectType.new(parameters)
This instantiation in RSpec 3.3 (the latest version) calls the after_save callback within that model though. This behavior does not match what actually happens in the development environment and what I expect when that new instance in memory is created. Additionally if I do a binding.pry on the test that this is ran on I can examine the database and in fact there is a persisted object in the database that is created by RSpec instead of just using the in memory object.
Why is this behavior occurring and how should I fix it?
UPDATE:
So it appears that if you have a dependency object that relies on the in memory ActiveRecord object and then that object is saved to the database, the in memory object will also be saved.
For example.
obj = ObjectType.new(parameters)
DependencyObject.create(relies_on: obj)
The obj object will be persisted to the database first and then the DependencyObject will be saved.
So it appears that if you have a dependency object that relies on the in memory ActiveRecord object and then that object is saved to the database, the in memory object will also be saved.
For example.
obj = ObjectType.new(parameters)
DependencyObject.create(relies_on: obj)
The obj object will be persisted to the database first and then the DependencyObject will be saved. This can lead to very strange callback behavior if you have a callback on obj on after_create or something it will get called at the execution of the save for DependencyObject and then the save for the other object will occur. Can be very frustrating.
I have a NSManagedObjectContext where two NSManagedObject are saved.
I'm calling a method in another thread and I need to access those two NSManagedObject so I created a child context like the following:
let childManagedContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
childManagedContext.parentContext = self.managedContext
When I do:
let myNSManagedObject1 = childManagedContext.objectWithID(self.myNSManagedObject1.objectID) as! MyNSManagedObject
let myNSManagedObject2 = childManagedContext.objectWithID(self.myNSManagedObject2.objectID) as! MyNSManagedObject
myNSManagedObject1 and myNSManagedObject2 are not the same objects as self.myNSManagedObject1 and self.myNSManagedObject2. Can someone explain me why?
Plus if I use existingObjectWithID instead of objectWithID, it seems I still have a fault object for my relationship in myNSManagedObject1 and myNSManagedObject2:
relationShipObject = "<relationship fault: 0x170468a40 'relationShipObject'>"
Understand that they are the "same" in the sense that they refer to the same object in your object graph. If you compare all attributes, you will find that they are equal.
However, because they are in different contexts, they will be two separate instances of this object. So the machine address you see will be different. I hope that clears up the confusion.
As for the "fault", that only means that the underlying object (or attribute) has not yet been fetched into memory. This is simply an optimization mechanism to minimize memory footprint. If you were to log the object or attribute explicitly, it would be fetched from the store and displayed as expected. See "Faulting and Uniquing" in the Core Data Programming Guide.
You have one object, that is the version that's in Core Data. When you use objectWithID: you create an instance of that object. So, if you do it twice you get two instances of the same object. (Much in the same way that you can create two objects of the same class.)
Of course, if you try to save your context, having changed one but not the other, weird things might happen.
A common pattern is where you create a new "editing" managed object context and create a new instance there. Then if the user pressed Cancel, you can just delete the context and not have to worry about rolling back any changes. I can't think where having two instances on the same context would be useful.
I'm new to core data and had a query.
If I call executeFetchRequest:error: to retrieve an entity from the context, and store this entity in a variable called A, and I repeat the process and store it the next time in a variable called B, will A and B refer to the same instance of the NSManagedObject i.e. will a change made to object A also be made to object B?
In addition, assuming I proceed to delete the entity from the managed object context, what would happen to these references?
Take a look at the Core Data Programming Guide section on Faulting and Uniquing. To quote:
Uniquing Ensures a Single Managed Object per Record per Context
Core Data ensures that—in a given managed object context—an entry in a persistent store is associated with only one managed object. The technique is known as uniquing. Without uniquing, you might end up with a context maintaining more than one object to represent a given record.
So, provided you execute the fetches on the same context, the returned results will point to the same instances.
When you delete an object, it is flagged for deletion until the next save operation, at which point it is deleted from the store. If you retain a reference to it thereafter, CoreData will throw an error if you try to access it. From the same document, in the section on creating and deleting objects:
You can find out if a managed object has been marked for deletion by sending it an isDeleted message. If the return value is YES, this means that the object will be deleted during the next save operation, or put another way, that the object is marked deleted for the current (pending) transaction.
the NSManagedObject subclasses are created and handled by the NSManagedContext by itself. It means you might possibly get the same instance of the NSManagedObject when you fetch them more time (but you don't know for sure). If you want to compare 2 objects, you should use some of your "isEqualTo:" implementations.
When you delete the object in that context, the contents of your properties will be removed. That means they will be set to nil if they are weak or they will point to deallocated memory if they are string. So it might happen that you app crashes when you try to access them via your properties(I've experienced that :)).
Every time you fetch an object you are obtaining different instances. To check if represents the same object you should compare objectID properties (to check if they are pointing to the same record in the persistent store).
If you delete one from context, you can check if the other is in context using existingObjectWithId:error
I would like to store some object in the session. I know, that there are at least two ways to do it:
in the service define scope = "session", and then define property def myObject
use httpSession: session['myObject'] = myObject
What is more useful approach to store object in the session?
Update: What the benefites of using of every method? Can I invalidate session if I would use scope = 'session'?
Update 2 If I would like to use object of another class in the service with session = "session", I have an exception about bean with scope session.
Based on the information you have provided storing an object in the session itself would be the simpler approach. Just be sure whatever you do store there is as small as possible.
session['someKey'] = new MyObject()
The documentation has some more information about using the session.
Unless you have a requirement for using an actual instance of an object you may find it easier to simply store a map in the session instead.
session['someKey'] = [mapKey1: 'value', meaningKey: 42]
...
println session['someKey'].meaningKey // 42