powershellpowershell-corepowershell-7.2

pwsh 7 format-default: Unable to cast object of type 'MySql.Data.Types.MySqlDateTime' to type 'System.IFormattable'


I need to read data from mysql database with pwsh 7. On powershell 5 it is working fine but with pwsh 7 not with , and receive an error message when querying a table with 'timestamp' data :

"format-default: Unable to cast object of type 'MySql.Data.Types.MySqlDateTime' to type 'System.IFormattable'."

 [void][system.reflection.Assembly]::LoadWithPartialName("MySql.Data")
        $connStr = "server=" + $MySQLHost + ";port=" + $MySQLPort + ";uid=" + $user + ";pwd=" + $pass + ";database=" + $MYSQLDatabase + ";Pooling=FALSE;Allow Zero Datetime=true;Allow User Variables=True;Connect Timeout=60"
        $conn = New-Object MySql.Data.MySqlClient.MySqlConnection($connStr)
        $conn.Open()
        New-Object MySql.Data.MySqlClient.MySqlCommand("USE $database", $conn)
        
    $query = "select Name, InputDate from ENVIRONMENT"
    $cmd = New-Object MySql.Data.MySqlClient.MySqlCommand($query, $conn)    
    $dataAdapter = New-Object MySql.Data.MySqlClient.MySqlDataAdapter($cmd)
    $dataSet = New-Object System.Data.DataSet
    $dataAdapter.Fill($dataSet, "data")
    $cmd.Dispose() 
    
    $dataSet.Tables["data"]  

So I can see that after "$dataAdapter.Fill($dataSet, "data")" data are collected from mysql because it return the number of row. But after "$dataSet.Tables["data"]" it return the error.

In powershell 5 it is working fine.

Any help :-) ?

Thanks


Solution

  • If your code works in Windows PowerShell, but not in PowerShell (Core) 7+, I encourage you to report an issue in the PowerShell GitHub repo.

    However, you should first make sure that you're loading the .NET (Core)-appropriate version of the assembly (MySql.Data) from the MySql.Data NuGet package, given that [System.Reflection.Assembly]::LoadWithPartialName("MySql.Data") may be loading an older, .NET Framework-only version from the GAC (Global Assembly Cache), as would the equivalent - and preferable - form Add-Type -AssemblyName MySql.Data.

    Unfortunately, using NuGet packages isn't well supported in PowerShell as of version 7.2.x:


    Some thoughts about the error and a potential - slow - workaround:

    MySql.Data.Types.MySqlDateTime indeed does not implement the System.IFormattable interface. It's unclear to me why this cast is even attempted or - in case the attempt is by design - why the exception isn't caught.

    When PowerShell stringifies objects, it checks for whether they implement the IFormattable, so that a culture-invariant string representation can be requested. Conceivably, the bug has to do with mistakenly believing MySqlDateTime to implement this interface.

    MySqlDateTime does have an explicit conversion operator to .NET's [datetime] (System.DateTime) type, which you should be able to use from PowerShell with a [datetime] cast.

    Thus, the following approach may work as a workaround:

    Note: The following is a simplified, self-contained example that simulates your MySQL data types; for simplicity, type [string] is used in lieu of [MySql.Data.Types.MySqlDateTime] to modify the columns of interest with an explicit [datetime] cast - adapt accordingly.

    # These sample rows simulate the following:
    #   $rows = $dataSet.Tables["data"]
    $rows = @(
      [pscustomobject] @{ 
        foo = 1; 
        bar = '1970/01/01'
      },
      [pscustomobject] @{ 
        foo = 2; 
        bar = '1970/01/02'
      }
    )
    
    # Get the first row, so the data types of its properties (columns)
    # can be analyzed.
    # (I assume there's a way to do this directly via a table object,
    #  and its column definitions, but I'm not familiar with the MySQL .NET API.)
    $firstRow = $rows | Select-Object -First 1
    
    # Construct an array of property names / calculated properties to pass
    # to Select-Object below.
    $propArray = foreach ($prop in $firstRow.psobject.Properties) {
      if ($prop.Value -is [string]) { # Substitute [MySql.Data.Types.MySqlDateTime] here.
        @{
          Name = $prop.Name
          Expression = [scriptblock]::Create("[datetime] `$_.$($prop.Name)")
        }
      }
      else {
        $prop.Name
      }
    }
    
    # Now output all rows with the [MySqlDateTime] columns (properties)
    # converted to [datetime].
    $rows | Select-Object $propArray