I have a PowerShell script that runs every hour, it essentially updates product info through a drip process. The script is failing in the dev environment, but continues to work in prod and qual. The failure point is at a line that uses LINQ. Here is the script:
$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)
Technically, it fails on the last line. but the second to last line is included because while researching this issue, I found a lot of other posts that pointed out if the two arguments in the ToLookup linq function are not of the same type, it can cause this issue. The second to last line was the "supposed" fix for many users, but sadly not myself. I still receive the
Cannot find an overload for "ToLookup" and the argument count: "2"
I checked any dependencies on all 3 environments, and everything is the same. And, the powershell versions are the same on all as well.
CodePudding user response:
Even though the error doesn't give much details, from your currently reproducible code we can tell that the issue is because $productInfos
is a single object of the type PSObject
and this class does not implement IEnumerable
Interface:
$productInfos = [pscustomobject]@{
product_name = 'Television'
price = 1000
product_id = 101
}
$productInfos -is [System.Collections.IEnumerable] # => False
To ensure $productInfos
is always an enumberable you can use the Array subexpression operator @( )
:
@($productInfos) -is [System.Collections.IEnumerable] # => True
Casting [object[]]
or [array]
should also be an option:
[object[]] $productInfos -is [System.Collections.IEnumerable] # => True
[array] $productInfos -is [System.Collections.IEnumerable] # => True
However above methods only work as long as $productInfos
has at least 1 element, as opposed to @(..)
which will always ensure an array:
The result is always an array of 0 or more objects.
To summarize:
$productInfos = [Linq.Enumerable]::ToLookup([object[]] $productInfos, $productIdDelegate)
$productInfos = [Linq.Enumerable]::ToLookup(@($productInfos), $productIdDelegate)
$productInfos = [Linq.Enumerable]::ToLookup([array] $productInfos, $productIdDelegate)
Are all valid options having what's explained above in consideration, if we're not sure if the variable will always be populated, the safest alternative will always be @(...)
:
# Returns Null instead of an error:
[Linq.Enumerable]::ToLookup(@(), $productIdDelegate)