首页 > 解决方案 > 有没有办法通过自定义 System.Management.Automation.ErrorRecord 抛出?

问题描述

我想System.Management.Automation.ErrorRecord用一些额外的属性来自定义:ContextMessageSqlCmdHeader.

我试图创建继承自的新类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
    }
}

但是当我通过这段代码抛出错误时:

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,
            $_))
}

我只是在System.Management.Automation.ErrorRecord没有我很棒的属性的情况下加入了来电者。看起来像是throw将我的对象投射到System.Management.Automation.ErrorRecord并丢失了属性。

我想请你们帮我在我的脚本中实现这个逻辑。任何帮助将不胜感激!

更新

我无法更改来电者的代码。它总是解析错误并将其写入日志:

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

我不能使用System.Exception,因为当我抛出创建的异常时,所有原始信息(例如InvocationInfoScriptStackTrace等等)ErrorRecord都丢失了。我找不到throw声明的源代码来理解它是如何工作的(也许有人可以指出我?)

我看到有一个TargetObject属性可以用来存储一些额外的信息,但是我看不到在声明之前将一些东西放到属性中的方法,throw因为该属性是ReadOnly并且我认为该属性不是为了存储额外的用户数据而设计的。

标签: powershell

解决方案


虽然System.Management.Automation.ErrorRecord该类在技术上不是密封的并且文档没有提到子类化,但看起来ErrorRecord实际上不支持子类化:

正如您所经历的那样,SqlRequestException您没有将原始实例throw放在调用堆栈中,大概是因为涉及复制到基类的实例中。

System.Exception改为子类并让 PowerShell 自动将您的异常包装在一个[ErrorRecord]实例中,.Exception您可以稍后查询其属性:

# 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)
"@
}

以上产生以下结果,表明两个自定义属性都已通过:

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

自定义异常的消息是原始异常的消息;要获取原始异常的详细信息,请访问$_.InnerException.


推荐阅读