Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there way to throw via custom System.Management.Automation.ErrorRecord?

Tags:

powershell

I'd like to throw custom System.Management.Automation.ErrorRecord with some additional properties: ContextMessage and SqlCmdHeader.

I tried to create new class inherited from System.Management.Automation.ErrorRecord:

class SqlRequestException : System.Management.Automation.ErrorRecord
{
    [hashtable]$SqlCmdHeader

    [string]$ContextMessage

    SqlRequestException() { }

    SqlRequestException(
        [string]$message,
        [hashtable]$sqlCmdHeader,
        [System.Management.Automation.ErrorRecord]$errorRecord
    ) : base($errorRecord, $null)
    {
        $this.SqlCmdHeader = $sqlCmdHeader
        $this.ContextMessage = $message
    }
}

But when I throw the error by this code:

try
{
    #exceptions is risen here
}
catch
{
    throw ([SqlRequestException]::New(
            "Some context message of exception which will help to understand the error without additional debugging of code",
            $sqlHeader,
            $_))
}

I got in caller just System.Management.Automation.ErrorRecord without my awesome properties. Looks like throw cast my object to System.Management.Automation.ErrorRecord and lost the properties.

I'd like to ask you guys to help me to implement this logic in my script. Any help will be much appreciate!

Update

I'm not able to change caller's code. It always parses error and write it to logs:

try
{
    # calling the method from module where I try to throw custom ErrorRecord
}
catch
{
    Write-Log -Text $_
}

I cannot use System.Exception because when I throw created exception, all original information (such as InvocationInfo, ScriptStackTrace and etc) from ErrorRecord is lost. I couldn't find source code of throw statement to understand how exactly it works (maybe someone can point me to it?)

I see there is TargetObject property which could be used to store some additional information, but I don't see ways to put something to the property before throw statement since the property is ReadOnly and I think the property is not designed to store additional user's data.

like image 509
Sergey K Avatar asked Dec 10 '25 22:12

Sergey K


1 Answers

While the System.Management.Automation.ErrorRecord class isn't technically sealed and the documentation doesn't mention subclassing, it looks like subclassing ErrorRecord is effectively not supported:

As you've experienced, the original SqlRequestException instance you throw doesn't make it as such up the call stack, presumably because copying into an instance of the base class is involved.

Subclass System.Exception instead and let PowerShell automatically wrap your exception in an [ErrorRecord] instance whose .Exception property you can later query:

# Make your custom exception derive from System.Exception.
class SqlRequestException : System.Exception
{
    [hashtable]$SqlCmdHeader
    [string]$ContextMessage

    # Construct the exception with the one passed as an 
    # argument as the *inner* exception.
    SqlRequestException(
        [string]$contextMessage,
        [hashtable]$sqlCmdHeader,
        [exception]$innerException
    ) : base($innerException.Message, $innerException)
    {
        $this.ContextMessage = $contextMessage
        $this.SqlCmdHeader = $sqlCmdHeader
    }
}

try { # Outer `try` to simulate a caller.
  try # Your code.
  {
      [int]::Parse('not a number') # Sample exception.
  }
  catch
  {
     # Throw your custom exception that wraps the original exception
     # ($_.Exception) via its .InnerException property.
     # PowerShell itself then automatically wraps your custom exception in
     # an [ErrorRecord] instance.
     throw ([SqlRequestException]::New(
              'custom message',
              @{ header1 = 'foo' },
              $_.Exception))
  }
} catch { # The (simulated) caller's catch block.
  # Now you can access your custom properties via $_.Exception.
  @"
Error: $_
Custom properties:
  ContextMessage: $($_.Exception.ContextMessage)
  SqlCmdHeader: $($_.Exception.SqlCmdHeader | Out-String)
"@
}

The above yields the following, showing that both custom properties were passed through:

Error: Exception calling "Parse" with "1" argument(s): "Input string was not in a correct format."
Custom properties:
  ContextMessage: custom message
  SqlCmdHeader: 
Name                           Value
----                           -----
header1                        foo

The custom exception's message is that of the original exception; to get the details of the original exception, access $_.InnerException.

like image 186
mklement0 Avatar answered Dec 13 '25 10:12

mklement0



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!