Работа с System.DBNull в PowerShell
Тратить больше времени на извлечение данных SQL в PowerShell. Проблемы с [System.DBNull]::Value и как PowerShell ведет себя с этим во время сравнений.
Вот пример поведения, которое я вижу, наряду с обходными путями
#DBNull values don't evaluate like Null...
if([System.DBNull]::Value){"I would not expect this to display"}
# The text displays.
if([string][System.DBNull]::Value){"This won't display, but is not intuitive"}
# The text does not display.
#DBNull does not let you use certain comparison operators
10 -gt [System.DBNull]::Value
# Could not compare "10" to "". Error: "Cannot convert value "" to type "System.Int32". Error: "Object cannot be cast from DBNull to other types.""
[System.DBNull]::Value -gt 10
# Cannot compare "" because it is not IComparable.
#No real workaround. Must use test for null workaround in conjunction to avoid comparison altogether:
[string][System.DBNull]::Value -and [System.DBNull]::Value -gt 10
#Example scenario with a function that uses Invoke-Sqlcmd2 to pull data
Get-XXXXServer | Where-Object{$_.VCNumCPUs -gt 8}
#Error for every line where VCNumCPU has DBNull value
#workaround
Get-XXXXServer | Where-Object{[string]$_.VCNumCPUs -and $_.VCNumCPUs -gt 8}
Я что-то упускаю, или нет "простого" обходного пути, который позволил бы людям с небольшим опытом использовать сравнения PowerShell, как и ожидалось?
Я представил предложение по Connect и получил временный обходной путь от Dave Wyatt, который преобразует datarow в psobjects с dbnulls, преобразованными в нули, но это добавляет немного накладных расходов. Похоже, что-то, что должно быть обработано под прикрытием, учитывая существующее "свободное" поведение PowerShell?
Любые советы, или я исчерпал свои варианты на данный момент?
Спасибо!
6 ответов
Самый простой способ $var -isnot [DBNull]
,
Я проверил это в моих собственных сценариях, и это работает как ожидалось.
Я думаю, что вы ошибаетесь подходом здесь. Как документально подтверждено, DBNull
класс представляет несуществующее значение, поэтому сравнения, такие как -gt
или же -lt
не имеет никакого смысла. Значение, которое не существует, не является ни большим, ни меньшим, чем любое заданное значение. Value
поле имеет Equals()
метод, который позволяет вам проверить, является ли значение DBNull
:
PS C:> ([DBNull]::Value).Equals(23)
False
PS C:> ([DBNull]::Value).Equals([DBNull]::Value)
True
Что я обычно делаю, так это:
[String]::IsNullOrWhiteSpace($Val.ToString())
Или это:
[String]::IsNullOrEmpty($Val.ToString())
Или это:
$Val.ToString() -eq [String]::Empty
Это часто работает просто отлично, так как [System.DBNull]::Value.ToString()
возвращает пустую строку, так что оба [String]::IsNullOrWhiteSpace([System.DBNull]::Value)
а также [System.DBNull]::Value.ToString() -eq [String]::Empty
оценивать как истинное
Очевидно, что они не являются логически эквивалентными, поскольку ваши данные могут на законных основаниях иметь пустые строки или могут представлять собой тип данных, который не имеет смысла как пустая строка (например, целое число). Однако, поскольку вы часто хотите обрабатывать DBNull точно так же, как пустые строки и строки только для пробелов, это может быть полезно, если вы достаточно хорошо знаете свои данные.
Если вы действительно хотите узнать, является ли значение DBNull, конечно, тогда используйте [DBNull]::Value.Equals($Value)
,
При работе с данными SQL в PS я включаю эту функцию и при необходимости вызываю:
function Check-IsNullWithSQLDBNullSupport ($var) {
if ($var -eq [System.DBNull]::Value -or $var -eq $null) {
return $true
} else {
return $false
}
}
Можно использовать так:
if (Check-IsNullWithSQLDBNullSupport -var $VarToBeTested) {
write-output "Is Null"
}
Кажется, я комментирую только старые сообщения, но я думаю, что ссылка на обсуждение с Дэйвом Вяттом выше не работает, я нашел ее здесь, перепроверив Google.
Код, над которым я сейчас работаю, не зависит от производительности, но мне нужно сравнить возвращаемые данные, чтобы сбросить свойства другого целевого объекта с другим типом.
Обычно удобный PowerShell вроде:
If( $SrcObject.Property ) { $TargObject.Property = $SrcObject.Property }
Это не работает с [DBNull]
Обычно я бы потратил время на то, чтобы посмотреть /dev, а затем использовать самый быстрый код, независимо от необходимости или сложности, но я должен получить rev1 как можно скорее. Прежде чем я даже осознал проблему [DBNull], я переворачивал объекты в [PSCustomObject], используя простой| Select $Props
$Props - это типизированный массив имен столбцов. Но это не меняет тип подсвойства, поэтому сравнение все равно не удается!
Учитывая, что я уже шел по пути, который предлагал Дэйв, я пошел еще немного.
$Props = ( $SQLData.Tables[0].Rows[0] | Get-Member -MemberType Properties ).Name
$Rows = $SQLData.Tables[0].Rows | Select $Props
ForEach( $RowObject in $Rows )
{
ForEach($Prop in $Props )
{
# Maybe: [String]::Empty below?
If( $RowObject.$Prop -is [DBNull] ) { $RowObject.$Prop = "" }
} #End Inner Loop.
} #End Outer Loop.
Примечание: это немного псевдо, потому что код продукта содержит строки в словаре, но этого должно быть достаточно, чтобы передать подход. Кроме того, приведенное выше не полностью протестировано, поскольку оно было переведено из рабочего кода.
Я не знаю, почему Get-Member не возвращает другие свойства, такие как RowError, RowState и т.д., но это работает, если вы не возражаете против преобразования [DBNull] в пустые строки. Кроме того, Get-Member стал более универсальным, без ввода реквизита...
Очевидно, что это не сильно отличается от некоторых приведенных выше приведений, но я, вероятно, не единственный, кто хочет припарковать некоторую сложность во вспомогательных функциях, поэтому "main" выглядит немного чище. Более того, пустая строка должна удовлетворять большинству сравнений позже, особенно с учетом того, что преобразование типов происходит в фоновом режиме.
Я знаю, что это комментарий, а не вопрос, но если у меня что-то не так, дайте мне знать. Я наткнулся на это, работая над активным проектом. Благодарность!
Некоторая команда | где FieldOfInterest - это DBNull Кажется, у меня работает. DBNull является типом, и оператор -is проверяет, имеет ли значение слева заданный тип.
Вы также можете использовать противоположную команду some | где FieldOfInterest - не DBNull