单条查询结果下PowerShell v5.1与v7.4的DataTable行数统计不一致
问题场景
有一个用于从数据库获取数据的PowerShell函数,在PowerShell v7.4中运行正常,但在v5.1环境下,当查询返回单条结果时,外部获取行数的行为不一致。
函数代码
function Get-Data ($connectstring, $sql) { # 创建OLEDB连接 $OLEDBConn = New-Object System.Data.OleDb.OleDbConnection($connectstring) $OLEDBConn.open() # 创建查询命令 $readcmd = New-Object system.Data.OleDb.OleDbCommand($sql,$OLEDBConn) $readcmd.CommandTimeout = '300' # 填充DataTable $da = New-Object system.Data.OleDb.OleDbDataAdapter($readcmd) $dt = New-Object system.Data.datatable [void]$da.fill($dt) $OLEDBConn.close() return $dt }
问题详情
返回多行结果时
执行查询:$qry= "SELECT * FROM backups" # 返回3行数据 $dtres = Get-Data $connString $qry $dtres.CountPowerShell v5.1(ISE)和v7.4(VS Code)均正确返回
3。返回单行结果时
执行查询:$qry= "SELECT 'test'" # 应返回1行数据 $dtres = Get-Data $connString $qry $dtres.Count- PowerShell v5.1返回
$null - PowerShell v7.4返回
1
- PowerShell v5.1返回
内部与外部访问Rows.Count的差异
在Get-Data函数内部,$dt.Rows.Count无论哪个版本都能正确返回1;但在外部执行:$qry= "SELECT 'test'" # 应返回1行数据 $dtres = Get-Data $connString $qry $dtres.Rows.Count两个版本均返回
0。
原因分析
这是PowerShell v5.1的特性:当函数返回的DataTable只包含一行数据时,PowerShell会自动解包这个DataTable,直接返回里面的DataRow对象,而非完整的DataTable。这导致外部变量$dtres实际是DataRow类型:
DataRow没有Count属性,因此$dtres.Count返回$nullDataRow的Rows属性为空,因此$dtres.Rows.Count返回0
PowerShell v7.4修复了这个自动解包行为,始终返回完整的DataTable对象。
解决方法
方法1:强制返回完整DataTable(推荐)
修改函数的返回语句,用逗号运算符包裹$dt,创建单元素数组阻止自动解包;或使用Write-Output -NoEnumerate参数强制返回原对象:
# 方式1:用逗号包裹 return ,$dt # 方式2:使用Write-Output参数 Write-Output -NoEnumerate $dt
修改后,外部拿到的始终是完整的DataTable对象,$dtres.Count和$dtres.Rows.Count都能正确返回行数。
方法2:外部强制类型转换
接收返回值时,显式指定变量类型为System.Data.DataTable,强制PowerShell将返回值转换为DataTable:
[System.Data.DataTable]$dtres = Get-Data $connString $qry $dtres.Count # 正确返回1 $dtres.Rows.Count # 正确返回1
方法3:返回DataRow集合(改变返回类型)
修改函数返回$dt.Rows,直接返回DataRowCollection,其Count属性在所有版本中都能正常工作:
function Get-Data ($connectstring, $sql) { # 原有代码不变... $OLEDBConn.close() return $dt.Rows }
此时外部执行$dtres.Count会直接返回行数,但该方法改变了函数返回类型,需确认调用方兼容性。
验证
以方法1为例,修改函数返回为return ,$dt后,在PowerShell v5.1中执行单行查询:
$qry= "SELECT 'test'" $dtres = Get-Data $connString $qry $dtres.Count # 返回1 $dtres.Rows.Count # 返回1
结果与v7.4一致,问题解决。
内容的提问来源于stack exchange,提问作者cafs98

