%Id() availability in %OnBeforeSave()
Is there documentation that states %Id() is supposed to be available in %OnBeforeSave()?
I am currently using it in this method without issues, but one of my colleagues is stating it is not supposed to be available for a new object at this point because the object has not been saved yet.
Comments
It's available if you update an already existing object.
NOT available for a New or Cloned object
Somewhat off-topic, there are reasons to watch out for %OnBeforeSave():
- The object is already serialized at this point, so you can't change any properties.
- The method is only triggered if the object was changed.
%OnAddToSaveSet() has neither of these gotchas (but you don't have %Id() there either, of course).
The ID is assigned by %SaveData() and %OnBeforeSave is invoked before %SaveData is called. For a new object the ID value will not be reliable until after %SaveData has returned. I do not know whether or not this is documented explicitly.
I defined this simple class:
Class Community.IdBeforeSave Extends (%Persistent, %Populate){Parameter USEEXTENTSET = 1;Parameter DEFAULTGLOBAL = "^C.I";Property Name As %String;Property DOB As %Date;Property SSN As %String;/// This callback method is invoked by the <METHOD>%Save</METHOD> method to /// provide notification that the object is being saved. It is called after /// the object's data has been successfully written to disk./// /// <P><VAR>insert</VAR> will be set to 1 if this object is being saved for the first time./// /// <P>If this method returns an error then the call to <METHOD>%Save</METHOD> will fail.Method %OnAfterSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]{set id = ..%Id()set ^C.S($increment(^C.S)) = "OnAfterSave, insert = "_$select(insert:"'true'",1:"'false'") _ ", ID = " _ $select(id'="":id,1:"<null>")Quit $$$OK}/// This callback method is invoked by the <METHOD>%Save</METHOD> method to /// provide notification that the object is being saved. It is called before /// any data is written to disk./// /// <P><VAR>insert</VAR> will be set to 1 if this object is being saved for the first time./// /// <P>If this method returns an error then the call to <METHOD>%Save</METHOD> will fail.Method %OnBeforeSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]{set id = ..%Id()set ^C.S($increment(^C.S)) = "OnBeforeSave, insert = "_$select(insert:"'true'",1:"'false'") _ ", ID = " _ $select(id'="":id,1:"<null>")Quit $$$OK}and then populated it with 5 objects. This is the result:
LATEST:USER>zw ^C.S ^C.S=10 ^C.S(1)="OnBeforeSave, insert = 'true', ID = <null>" ^C.S(2)="OnAfterSave, insert = 'true', ID = 1" ^C.S(3)="OnBeforeSave, insert = 'true', ID = <null>" ^C.S(4)="OnAfterSave, insert = 'true', ID = 2" ^C.S(5)="OnBeforeSave, insert = 'true', ID = <null>" ^C.S(6)="OnAfterSave, insert = 'true', ID = 3" ^C.S(7)="OnBeforeSave, insert = 'true', ID = <null>" ^C.S(8)="OnAfterSave, insert = 'true', ID = 4" ^C.S(9)="OnBeforeSave, insert = 'true', ID = <null>" ^C.S(10)="OnAfterSave, insert = 'true', ID = 5"
HTH,
Dan
A new persistent object will have its ID created in %SaveData which is called after %OnBeforeSave(), if you look in the code behind you will see this...
If 'partial { Set sc=..%OnBeforeSave(%objTX(1,+$this,4)) Throw:('sc) ##class(%Exception.StatusException).ThrowIfInterrupt(sc) }Set id=$listget(serial),sc=..%SaveData(.id) If ('sc) { Throw ##class(%Exception.StatusException).ThrowIfInterrupt(sc) }So this shouldn't work for you, unless the object has already been persisted.
So using ..%Id(), (which is returning the actual ID for the new object ) in %OnBeforeSave(), is not supposed to be working at this point and should be returning a null value instead?
Thank you everyone for your help. We greatly appreciate it!