Home > database >  Hashtable key depends on where function is defined
Hashtable key depends on where function is defined

Time:08-31

I'm encountering the strangest issue with powershell code.

I have 2 files, A.ps1 and B.ps1. B contains very simple code that maps environment to days of the week, and then uses the current environment to get the correct days:

$Location\A.ps1 # import Detect-Environment
$currentEnv = Detect-Environment

# 0 as Sunday, 6 as Saturday
$activatedEnvsAndWeekdays = @{
    [CurrentEnv]::LOCAL = @(0, 1, 2, 3, 4, 5, 6)
    [CurrentEnv]::DEV   = @(0, 1, 2, 3, 4, 5, 6)
    [CurrentEnv]::UAT   = @()
    [CurrentEnv]::PROD  = @(1)
}

$activatedWeekdayNums = $activatedEnvsAndWeekdays[$currentEnv]

A.ps1 contains:

enum CurrentEnv {
    LOCAL
    DEV
    UAT
    PROD
}

function Detect-CinchyEnvironment {
    $hostName = [System.Net.Dns]::GetHostName()
    switch -Wildcard ($hostName) {
        '*CINCHYPRD*' { [CurrentEnv]::PROD }
        '*CINCHYUAT*' { [CurrentEnv]::UAT }
        '*CINCHYDEV*' { [CurrentEnv]::DEV }
        Default { [CurrentEnv]::LOCAL }
    }
}

Since my current env is [CurrentEnv]::LOCAL, $activatedWeekdayNums should be the list @(0, 1, 2, 3, 4, 5, 6). But it's not! It's $null. After much digging, I discovered why.

When I define and call Detect-Environment from within my file, the code works fine. But when I define Detect-Environment in another file called A.ps1 and then import it with . $Location\A.ps1, it doesn't work, even though $currentEnv says it's equal to [CurrentEnv]::LOCAL. Indeed if I compare the variables obtained by equating$currentEnv1 (local function) to $currentEnv2 (imported function), $currentEnv2 -eq $currentEnv return True. But the result with $activatedEnvsAndWeekdays is different. Totally confused.

Any help is appreciated! Happy to provide more context/code.

EDIT: $currentEnv1.GetType() is equal to:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     CurrentEnv                               System.Enum

$currentEnv2.GetType() is equal to the same thing:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     CurrentEnv                               System.Enum

EDIT2: To add more info, even if I do $activatedEnvsAndWeekdays[[CurrentCinchyEnv]::LOCAL] so I'm not using $currentEnv at all, it doesn't return anything when Detect-Environment and enum CurrentCinchyEnv are imported.

CodePudding user response:

Posting this answer as was requested in comments. For anyone wondering how to reproduce this bug (?), here is a minimal reproducible example.

  • script1.ps1
enum TestEnum {
    Test
}

$key = . .\script2.ps1
$map = @{ [TestEnum]::Test = 'hello' }
$map[$key]
  • script2.ps1
[TestEnum]::Test

If we attempt to run script1.ps1 in a clean PS session we should get the right value hello, however, any subsequent update to script1.ps1 (has to be the .ps1 defining the enum) will result in PowerShell emitting a new type with the same name:

PS /> [AppDomain]::CurrentDomain.GetAssemblies().GetTypes() |
    Where-Object Name -EQ TestEnum

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     TestEnum                                 System.Enum
True     True     TestEnum                                 System.Enum

And a newer assembly version:

PS /> [AppDomain]::CurrentDomain.GetAssemblies().GetTypes() |
    Where-Object Name -EQ TestEnum | ForEach-Object AssemblyQualifiedName

TestEnum, PowerShell Class Assembly, Version=1.0.0.2, Culture=neutral, PublicKeyToken=null
TestEnum, PowerShell Class Assembly, Version=1.0.0.3, Culture=neutral, PublicKeyToken=null

It's worth noting that this would only affect us while working on our code but the bug should not be reproducible on a clean session, in other words, if you wish to use reference type keys on your hashtable and can bear with having to restart your PowerShell session on each update of your code then it should be fine during runtime.

As a personal recommendation, I would use strings as the hashtable keys to avoid this.

As workaround, casting the type should fix the issue without a need to restart the session:

$map[[TestEnum] $keyEnum]
  • Related