Powershell and SQL parameters. If empty string, pass DBNull

后端 未结 4 464

I got this parameter:

$objDbCmd.Parameters.Add(\"@telephone\", [System.Data.SqlDbType]::VarChar, 18) | Out-Null;
$objDbCmd.Parameters[\"@telephone\"].Value =         


        
4条回答
  •  执念已碎
    2021-01-18 07:59

    Many years later, let me clarify:

    Josh's answer shows a helpful simplification for testing strings for emptiness (relying on PowerShell's implicit to-Boolean conversion[1]), but it is unrelated to Tommy's (the OP's) problem.

    Instead, the error message

    "Failed to convert parameter value from a ResultPropertyValueCollection to a String."

    implies that it is the non-null case that caused the problem, because $objDbCmd.Parameters["@telephone"].Value expects either a string value or [DBNull]::Value, whereas $objUser.Telephone is of type [ResultPropertyValueCollection], i.e. a collection of values.

    Thus, in the non-null case, a string value must be assigned, which must be derived from the collection; one option is to take the first collection element's value, another would be to join all values with a separator to form a single string, using, e.g., [string]::Join(';', $objUser.Telephone) or, if joining the elements with spaces is acceptable (not a good idea with multiple phone numbers), simply with "$($objUser.Telephone)".[2]

    Detecting an empty collection via [string]:IsNullOrEmpty() actually worked, despite the type mismatch, due to how PowerShell implicitly stringifies collections when passing a value to a [string] typed method parameter.[2]

    Similarly, using implicit to-Boolean conversion works as expected with collections too: an empty collection evaluates to $false, a non-empty one to $true (as long as there are either at least two elements or the only element by itself would be considered $true[1])

    Therefore, one solution is to use the first telephone number entry:

    $objDbCmd.Parameters["@telephone"].Value = if ($objUser.Telephone) {
        $objUser.Telephone[0].ToString()  # use first entry
      } else {
        [DBNull]::Value
      }
    

    Note: If $objUser.Telephone[0] directly returns a [string], you can omit the .ToString() call.

    In PowerShell v7+ you can alternatively shorten the statement via a ternary conditional:

    $objDbCmd.Parameters["@telephone"].Value =
      $objUser.Telephone ? $objUser.Telephone[0].ToString() : [DBNull]::Value
    

    [1] For a comprehensive summary of PowerShell's automatic to-Boolean conversions, see the bottom section of this answer.

    [2] When implicitly converting a collection to a string, PowerShell joins the stringified elements of a collection with a single space as the separator by default; you can override the separator with the automatic $OFS variable, but that is rarely done in practice; e.g., array 'foo', 'bar' is converted to 'foo bar'; note that this conversion does not apply when you call the collection's .ToString() method explicitly, but it does apply inside expandable (interpolating) strings, e.g., "$array".

提交回复
热议问题