我有一个PowerShell脚本,每小时运行一次,它基本上通过drop进程更新产品信息。该脚本在dev环境中失败,但在prod和qual环境中继续工作。失败点在使用LINQ的一行。以下是该脚本:
$productInfos = [pscustomobject]@{
product_name = 'Television'
price = 1000
product_id = 101
} | ForEach-Object{$_}
$productIdDelegate = [Func[object,int]] {$args[0].product_id}
$productInfos = [Linq.Enumerable]::ToLookup($productInfos, $productIdDelegate)
从技术上讲,它在最后一行失败。但是倒数第二行被包括在内,因为在研究这个问题的时候,我发现了很多其他的帖子,指出如果ToLookup linq函数中的两个参数不是相同的类型,它可能会导致这个问题。倒数第二行是许多用户的“假定”修复,但遗憾的是不是我自己。我仍然收到
找不到“ToLookup”和参数计数的重载:“二”
我检查了所有3个环境的任何依赖关系,一切都是一样的。而且,powershell版本在所有环境上也是一样的。
2条答案
按热度按时间kmbjn2e31#
尽管这个错误没有给出太多细节,但是从当前可重现的代码中,我们可以看出这个问题是因为
$productInfos
是PSObject
类型的单个对象,而这个类没有实现IEnumerable
Interface:要确保
$productInfos
始终是 * enumberable *,可以使用Array子表达式运算符@( )
:铸造
[object[]]
或[array]
也应该是一个选项:但是,上述方法仅在
$productInfos
至少有1个元素时才有效,而@(..)
始终确保数组:结果永远是0或多个对象的数组。
总结一下:
是否所有有效选项都考虑了上面所解释的内容,如果我们不确定变量是否总是被填充,那么最安全的选择总是
@(...)
:zlwx9yxi2#
前面有一个注意事项:
... | ForEach-Object{ $_ }
,带有一个 single 输入对象,就像您的例子一样,不会输出一个 array(除非输入对象是一个数组 * 作为一个整体 *,这是不典型的),即使输入是一个单元素数组,因为管道的枚举行为。事实上,管道 * 本身 * 从不在输出上创建数组,它 * 一个接一个地 * 流对象,并且只有当该流被 * 捕获 * 时,才必须创建数组--但仅针对 * 两个或更多 * 输出对象;有关背景信息,请参见this answer。Santiago Squarzon提供了关键的指针:通过将传递给
[System.Linq.Enumerable]::ToLookup[TSource, TKey]()
的第一个参数 Package 在[object[]]
数组中,PowerShell可以找到此方法的正确重载和 * 类型参数 *:与所有LINQ方法一样,
ToLookup
:PowerShell必须在调用中 * 推断 * 类型参数,因为您没有 * 显式 * 提供类型参数,而在C#中则需要这种方式(例如
Array.Empty<int>()
)您所针对的
ToLookup
多载的签章为(您可以执行[Linq.Enumerable]::ToLookup
,也就是 * 不含()
*,就可以看到这个签章,不过为了便于阅读,下列签章已经过简化和重新格式化):由于您传递给
keySelector
参数的参数$productIdDelegate
是[Func[object,int]]
的示例,因此PowerShell可以推断类型参数TSource
的参数是[object]
,而TKey
的参数是[int]
。由于
TSource
是[object]
,因此传递给source
参数的参数必须是IEnumerable[object]
类型。您的
$productInfos
值,由于是一个单一对象,* 不 * 实现IEnumerable[object]
接口,但是[object[]]
类型的 * 数组 * 实现了。如果数组子表达式运算符
@(...)
总是返回[object[]]
类型的数组,则使用它 Package$productInfos
就足够了。请注意,如果您使用 * 特定 * 类型来代替
[object]
,例如[Func[datetime, int]]
,@(...)
将 * 无法 * 工作:那么您将需要一个特定于类型的 cast,例如本例中的[datetime[]]
。相反,在您的情况下,您可以使用
[object[]] $productInfos
(或其等效项[Array] $productInfos
)作为@($productInfos)
的替代项。另请参阅:
PowerShell 7.3及更高版本的替代产品:
7.3及以上版本具有- optional -支持显式*指定类型参数,这是一个好消息,原因有二:
System.Array.Empty[T]
方法就是一个简单的示例,现在可以将其称为[Array]::Empty[int]()
如果将此语法应用于调用,则可以省略将脚本块强制转换为
[Func[object, int]]
的操作,因为此强制转换是由显式类型参数 * 隐含 * 的;使用简单、独立的呼叫版本:与 inferred 类型参数的情况一样,如果第一个类型参数不是
[object]
,比如[pscustomobject]
,则需要类型特定的数组转换(仅@(...)
是不够的):