Question Richard Sr. Boroczky · Nov 8, 2017

%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

Robert Cemper · Nov 8, 2017

It's available if you update an already existing object.

NOT available for a New or Cloned object

0
Otto Medin  Nov 8, 2017 to Robert Cemper

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).

0
Dan Pasco · Nov 8, 2017

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

0
Sean Connelly · Nov 8, 2017

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.

0
Richard Sr. Boroczky · Nov 8, 2017

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? 

0