Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

There is now a way to throw an exception in ExpressX code that can be caught in the edmiNET binding's Execute function of the query object from the xpx machine. .
Works on externally callable the query functions in ExpressX that has generic return type (generic or aggregate of generic).
To raise an .NET xpxException in edmiNET you need to create a view_entity like this View_Entity Exception (as shown below) and return it in an aggregateAggregate Of Exception.
We use Aggregate of Exception to allow for more than one exception as a chain of inner exceptions:

Code Block
titleException View Entity
linenumberstrue
---------------------------------------------------------------------------------------------
-- Exception handling
-- In the global section declare the following:
-- GLOBAL
--    GExceptions                : Aggregate Of Exception := [];
--    GException                 : Exception;
--    ...
-- END_GLOBAL;
 
VIEW_ENTITY Exception;
   _Type_       : STRING;  -- Either 'edmException' or 'xpxException'
   errCode      : INTEGER; -- EDM error code as returned from xpxExceptionId
   errMessage   : STRING;  -- EDM error message as returned from xpxExceptionId
   userCode     : INTEGER; -- Selfmade code
   userMessage  : STRING;  -- Selfmade message
   functionName : STRING;  -- Function as returned from xpxExceptionId or selfmade
   lineNumber   : INTEGER; -- Line number as returned from xpxExceptionId or selfmade.
   innerException : Exception;
END_VIEW_ENTITY;

In the ExpressX code

...

titleExpress Helper functions and procedures
linenumberstrue
collapsetrue

...

-- Create an new innermost exception representing the current xpxExceptionId object
Function GetEDMException : Aggregate Of Exception; 
   GExceptions := [];
   CreateEDMException;
   Return(GExceptions);
End_Function;

Procedure NewEDMException;
   GExceptions := [];
   CreateEDMException;
End_Procedure;

-- Create an new "selfmade" innermost exception 
Function GetException(Message : String) : Aggregate Of Exception; 
   GExceptions := [];
   NewOuterException(Message);
   Return(GExceptions);
End_Function;

Procedure NewException(Message : String);
   GExceptions := [];
   NewOuterException(Message);
End_Procedure;

-- Create an new outer exception on the previous (inner) exception 
Function GetOuterException(Message : String) : Aggregate Of Exception; 
   NewOuterException(Message);
   Return(GExceptions);
End_Function;

Procedure NewOuterException(Message : String);
   Local
      lInnerException : Exception;
   End_Local;
   lInnerException := GException;
   New GException;
   GException._Type_         := 'xpxException';
   GException.userMessage    := Message;
   GException.innerException := lInnerException;
   GExceptions ++ GException;
End_Procedure;

Procedure CreateEDMException;
   Local
      lInnerException : Exception;
   End_Local;
   lInnerException := GException;
   New GException;
   GException._Type_       := 'edmException';
   GException.errCode      := xpxExceptionId.errCode;
   GException.errMessage   := xpxExceptionId.errMessage;
   GException.functionName := xpxExceptionId.functionName;
   GException.lineNumber   := xpxExceptionId.lineNumber;
   GException.innerException := lInnerException;
   GExceptions ++ GException;
End_procedure;

we recommend the following two exception handling utility functions:

  • Function GetException(Message : String) : Aggregate Of Exception
  • Procedure CancelException


Function GetException(Message : String) : Aggregate Of Exception;
   Local
      lInnerException : Exception;
      lMessage : String;
      lKeep : Boolean := True;
   End_Local;
   If NVL(xpxExceptionId.errCode, 0) <> 0 Then
      -- EDM error
      -- Avoid reporting the same error twice
      Repeat I := 1 To xpfSizeOf(GExceptions);
         If (GExceptions[I].errCode = xpxExceptionId.errCode) And
            (GExceptions[I].lineNumber = xpxExceptionId.lineNumber) Then
            lKeep := False;
            Escape;
         End_If;
      End_Repeat;
      If lKeep Then
         lInnerException := GException;
         New GException;
         GException._Type_         := 'edmException';
         GException.errCode        := xpxExceptionId.errCode;
         GException.errMessage     := xpfGetErrorText(xpxExceptionId.errCode);
         GException.functionName   := xpxExceptionId.functionName;
         GException.lineNumber     := xpxExceptionId.lineNumber;
         GException.innerException := lInnerException;
         lMessage := xpfStringPrintf('Line #%d: Exception. EDM Error number: %d, Message: %s', NVL(xpxExceptionId.lineNumber, xpxCurrentLine), GException.errCode, GException.errMessage);
         xpxPrintf('%s\n', lMessage); xpxDebugPrintf('%s\n', lMessage);
         GExceptions ++ GException;
       End_If;
   End_If;
   If NVL(Message, '') <> '' Then
      lInnerException := GException;
      New GException;
      GException._Type_         := 'xpxException';
      GException.userMessage    := Message;
      GException.innerException := lInnerException;
      lMessage := xpfStringPrintf('Line #%d: Exception: Message: %s', NVL(xpxExceptionId.lineNumber, xpxCurrentLine), GException.userMessage);
      xpxPrintf('%s\n', lMessage); xpxDebugPrintf('%s\n', lMessage);
      GExceptions ++ GException;
   End_If;
   Return(GExceptions);
End_Function;

Code Block
titleExpress Helper functions and procedures
linenumberstrue
collapsetrue
 
-- To ignore an exception from lower level and continue
-- execution as normal call the following function.
Procedure CancelException;
   GExceptions := [];
   GException := ?;
End_Procedure;
---------------------------------------------------------------------------------------------


Declare the follwing On_Error_Do block in the beginning of every function to ensure that all EDM errors are caught and that exceptions propagate upwards in the stack.
      On_Error_Do xpxThrow(GetException(?)); End_On_Error_Do;


Throw "soft" errors from code like this:
     xpxThrow(GetException('Something when wrong'));


To ignore an exception and reset exception chain:
     On_Error_Do cancelException; End_On_Error_Do;


Catch the XPXException in edmiNET like this:
   
edmiNet VB:

Code Block
languagevb
title.Net VB Example
linenumberstrue
Try
   EnterpriseModel.NewQuery.Execute("CatDictionaryServices", "UpdateExchangeRequirementConceptAttribute", New Object() {New Instance(ProductPage.ERConceptId),
                                                                                                                                     New Instance(lGroupId),
                                                                                                                                     New Instance(lConceptId),
                                                                                                                                     Column.Name, NewValue})
    RefreshRow = True
Catch XPXEx As XPXException
    XtraMessageBox.Show(XPXEx.Message)
Catch Ex As Exception
    XtraMessageBox.Show(Ex.ToString)
End Try

edmiNet C#:

Code Block
languagec#
title.NET C# Example
linenumberstrue
try {
		EnterpriseModel.NewQuery.Execute("CatDictionaryServices", "UpdateExchangeRequirementConceptAttribute", new object[] {
			new Instance(ProductPage.ERConceptId),
			new Instance(lGroupId),
			new Instance(lConceptId),
			Column.Name,
			NewValue
		});
		RefreshRow = true;
} catch (XPXException XPXEx) {
		XtraMessageBox.Show(XPXEx.Message);
} catch (Exception Ex) {
		XtraMessageBox.Show(Ex.ToString);
}

If there is a usermessage in the xpxException object this will be shown as the Message of the exception object. If there is no usermessage and there is a edmErrroCode the message will look like this:

Code Block
titleExample output
Error number 11298: Illegal data type or data value of argument #1.
Function: xpfGetInstanceModel
Line number: 1581
Query function: CatDictionaryServices.UpdateExchangeRequirementConceptAttribute