Blaise ZARKA · Dec 19, 2019

%New error handling


On a persistent class, I have defined an %OnNew method that validate some properties with appropriate status code for each kind of error.
Question is: how do you catch the specific error when %New is called on this class?

Surrounding %New method with a try-catch not seems to work.


5 0 8 206


The error should be in the %objlasterror variable. You would have to check: if %New() returned a new instance, if not then check the value of %objlasterror. Then perform any error handling you'd like to do. 

try/catch doesn't work because %New() doesn't throw any exceptions.

Interesting point Jenna. Thanks.
I'll try that!

While this is certainly possible, you'd be breaking the "contract". %OnNew() is expected to return a %Status that will be handled by %New().  This means the exception will be thrown by %OnNew()  but %New() won't care about it and still return an object (unless something else prevents it from doing so). 

I've changed my recommendation on this a little. Return the result of %ValidateObject() as the final argument, but don't return that status as the return value of the method. That way, if there are any required properties, and the call to %New() doesn't supply them, %OnNew() still works. Here's the updated example:

/// constructor
Method %OnNew(name As %String = "", phone As %String = "", dob as %Date, Output valid As %Status) As %Status [Private]
    set valid = $$$OK
    set ..Name = name
    set ..Phone = phone
    set ..DOB = dob
    set valid = ..%ValidateObject() // validate the new object
    return $$$OK

The proper way to do this would be

if the %OnNew is going to error you should throw an exception-  If you did this, the thrown exception should be caught by your try/catch block that calls the %New() method.  Then in the catch block you can examine either %objlasterror or perhaps some specific error variable you create for your purposes.

%OnNew() must return a %Status to %New(). But the code that calls %New() also needs the %Status. I think a "best practice" is simply to add an Output  %Status argument to %OnNew() that will therefore be returned to %New(). So the %Status is being returned using return (for %New()) and by using a pass-by-reference argument (for the code that calls %New()).

/// constructor
Method %OnNew(name As %String = "", phone As %String = "", dob as %Date, Output st As %Status) As %Status [Private]
    set st = $$$OK
    set ..Name = name
    set ..Phone = phone
    set ..DOB = dob
    set st = ..%ValidateObject() // validate the new object
    return st

Hi Blaise,

You can pass %OnNew expection to %New with this code snippet :

Class Test.PersitenceTest Extends %Persistent
Property mandatory As %String [ Required ];
Method %OnNew(mandatory As %String = "") As %Status
set ..mandatory = mandatory
Quit $$$OK

 Now when you call the new method :

USER>set test = ##class(Test.PersitenceTest).%New()

  If $isobject($get(newerror))=1 Throw newerror
<THROW>%Construct+9^Test.PersitenceTest.1 *%Exception.StatusException ERROR #5659: Property 'Test.PersitenceTest::mandatory(10@Test.PersitenceTest,ID=)' required

Now you can catch you %OnNew expection anywhere :

ClassMethod test() As %Status
try {
set test = ##class(Test.PersitenceTest).%New()
catch ex {
Return ex.AsStatus()
Return $$$OK