It’s a little bit of a hack, but casting the value to [string] seems to work without adding much code. DBNull and $null both become the empty string, which evaluates to $false:
Get-XXXXServer | Where-Object { [string]$_.VCServerContact }
That’s essentially the same thing that you’re doing with the -notlike “” option; PowerShell’s casting the value to a string for you before evaluating the notlike operator.
As for outputting custom objects and simultaneously scrubbing out DBNull (with a pure PowerShell implementation), here’s the fastest option I’ve found, based on some quick tests (snip of modified Invoke-SQLCMD2):
switch ($As)
{
'DataSet' { Write-Output ($ds) }
'DataTable' { Write-Output ($ds.Tables) }
'DataRow' { Write-Output ($ds.Tables[0]) }
'PSObject' {
$properties = New-Object System.Collections.Specialized.OrderedDictionary
foreach ($row in $ds.Tables[0].Rows)
{
$properties.Clear()
foreach ($property in $row.PSAdapted.PSObject.Properties)
{
if ($property.Value -is [System.DBNull])
{
$value = $null
}
else
{
$value = $property.Value
}
$properties[$property.Name] = $value
}
[pscustomobject] $properties
}
}
'SingleValue' { Write-Output ($ds.Tables[0] | Select-Object -Expand $ds.Tables[0].Columns[0].ColumnName ) }
}
This seems to take about 10x as long as the ‘DataRow’ option. Note that the [pscustomobject] syntax requires at least PowerShell 3.0, but going with the 2.0-compatible syntax of New-Object psobject -Property $properties more than doubled the execution time in my tests. Likewise, using Write-Output also added a pretty significant amount of time as compared to just letting PowerShell do the pipeline output itself.
There’s quite a bit of overhead involved in simply accessing members of objects in PowerShell. Taking the logic from that block and moving it out into a C# function is much, much faster:
$cSharp = @'
using System;
using System.Data;
using System.Management.Automation;
public class DBNullScrubber
{
public static PSObject DataRowToPSObject(DataRow row)
{
PSObject psObject = new PSObject();
if (row != null && (row.RowState & DataRowState.Detached) != DataRowState.Detached)
{
foreach (DataColumn column in row.Table.Columns)
{
Object value = null;
if (!row.IsNull(column))
{
value = row[column];
}
psObject.Properties.Add(new PSNoteProperty(column.ColumnName, value));
}
}
return psObject;
}
}
'@
Add-Type -TypeDefinition $cSharp -ReferencedAssemblies 'System.Data','System.Xml'
# Snip from Invoke-SqlCmd2
switch ($As)
{
'DataSet' { Write-Output ($ds) }
'DataTable' { Write-Output ($ds.Tables) }
'DataRow' { Write-Output ($ds.Tables[0]) }
'PSObject' {
foreach ($row in $ds.Tables[0].Rows)
{
[DBNullScrubber]::DataRowToPSObject($row)
}
}
'SingleValue' { Write-Output ($ds.Tables[0] | Select-Object -Expand $ds.Tables[0].Columns[0].ColumnName ) }
}